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