存储库模式实现

本文关键字:实现 模式 存储 | 更新日期: 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);
}

还要考虑单元测试。具体方法比智商容易模拟得多。

我实际上认为第一个更好。我根据以下因素做出决定:

  1. 如果产品结构将被重构:

    • IQueryable 方法 - 您只需要更改代码中的调用方法。
    • IEnumerables - 您还需要更改方法名称。

  2. 如果许多人要派生你想要以多态方式迭代的接口:

    • IQueryable 方法 - 通用统一方法名称的好处
    • IEnumerables - 某些名称可能不描述所需的方法。

  3. 弹性

    • IQueryable方法-易于分为IEnumerables方法。
    • IEnumerables方法 - 很难转换回IQueryable方法。

因此,我建议您从IQueryable作为默认选择开始,随着代码的进展,您可以随时更改为所需的更具体的IEnumerables方法。