存储库模式实现
本文关键字:实现 模式 存储 | 更新日期: 2023-09-27 18:31:18
似乎我找到的每个存储库模式示例,实现在某些方面都是不同的。以下是我主要找到的两个例子。
interface IProductRepository
{
IQueryable<Product> FindAll();
}
然后通常还有另一层与存储库通信并调用 FindAll() 方法并执行任何操作,例如查找以字母"s"开头的产品或获取特定类别中的产品。
我发现的另一个例子很多 将所有查找方法放入存储库
interface IProductRepository
{
IEnumerable<Product> GetProductsInCategory(int categoryId);
IEnumerable<Product> GetProductsStartingWith(string letter);
IEnumerable<PromoCode> GetProductPromoCodes(int productId);
}
你建议我走哪条路?或者彼此的优点/缺点是什么?
根据我的理解,阅读 http://martinfowler.com/eaaCatalog/repository.html 第一种方法似乎最能反映这一点?
第一个很可怕。 IQueryable
就像一个上帝的物体。真的很难找到它的 100% 完整实现(即使在所有 OR/M 中也是如此)。您可以直接公开您的ORM而不是使用它,否则您可能会得到一个泄漏的抽象层。
乔尔说得最好(文字来自维基百科文章):
在Spolsky的文章中,他提请注意许多抽象的例子,这些例子大部分时间都有效,但底层复杂性的细节不容忽视,从而将复杂性带入了本应由抽象本身简化的软件中
。
乔尔斯博客条目
第二种方法更容易实现并保持抽象完整。
更新
您的仓库违反了单一责任原则,因为它有两个更改原因。第一个是产品 API 是否更改,另一个是促销代码 API 是否更改。恕我直言,您应该使用两个不同的存储库,例如:
interface IProductRepository
{
IEnumerable<Product> FindForCategory(int categoryId);
IEnumerable<Product> FindAllStartingWith(string letter);
}
interface IPromoCodeRepository
{
IEnumerable<PromoCode> FindForProduct(int productId);
}
改变的东西:
- 我倾向于在返回多个项目时以
Find
开始方法,如果返回单个项目Get
方法。 - 较短的方法名称 = 更易于阅读。
- 单一责任。更容易判断使用存储库的类对依赖项具有什么。
定义良好的小型接口可以更轻松地发现违反 SOLID 原则的行为,因为破坏原则的类往往会得到臃肿的构造函数。
共识正在建立:一路第二选择。除了查询逻辑到处泄漏IQueryable之外,正确实现它的困难,测试和模拟非常困难。
就个人而言,我建议使用第二个示例,这样您就可以将搜索逻辑封装在一个地方,并且调用者的意图由他们调用的方法的名称明确定义。如果使用第一个示例,则查询代码将泄漏整个应用程序,最终将重复查询。
我建议避免重复。这是第一个目标。如果你有逻辑在几个地方找到以某个字母开头的产品,那么这是一个特例,值得提取到单独的方法中(它也为您的特定情况提供了很好的描述)。没有重复的代码更易于更改、理解和维护。
因此,我倾向于使用一种通用的搜索方法,其中包含IQueryable
和一组多次使用的方法:
interface IRepository<T>
{
IQueryable<T> FindAll();
}
interface IProductRepository : IRepository<Product>
{
IEnumerable<Product> GetProductsInCategory(int categoryId);
IEnumerable<Product> GetProductsStartingWith(string letter);
IEnumerable<PromoCode> GetProductPromoCodes(int productId);
}
还要考虑单元测试。具体方法比智商容易模拟得多。
我实际上认为第一个更好。我根据以下因素做出决定:
-
如果产品结构将被重构:
- IQueryable 方法 - 您只需要更改代码中的调用方法。
- IEnumerables - 您还需要更改方法名称。
-
如果许多人要派生你想要以多态方式迭代的接口:
- IQueryable 方法 - 通用统一方法名称的好处
- IEnumerables - 某些名称可能不描述所需的方法。
-
弹性
- IQueryable方法-易于分为IEnumerables方法。
- IEnumerables方法 - 很难转换回IQueryable方法。
因此,我建议您从IQueryable作为默认选择开始,随着代码的进展,您可以随时更改为所需的更具体的IEnumerables方法。