【问题标题】:Comparison of Specification Pattern, Func<T,bool> Predicates and Pipes & Filters规范模式、Func<T,bool> 谓词与管道和过滤器的比较
【发布时间】:2010-08-26 23:51:17
【问题描述】:

我正在做一些研发工作,因此正在探索设计模式。我最近一直在阅读规范模式,并被提及 this 很棒的文章。

我对代码的简洁性和简洁性很感兴趣,但我开始对使用其他技术实现相同的简洁性进行比较。

考虑以下服务层的接口契约:

public interface IFooDataService
{
   ICollection<Foo> GetFoosBySpecification(Specification<Foo> specification);
   ICollection<Foo> GetFooByPredicate(Func<Foo,bool> predicate);
   ICollection<Foo> GetFooBySearchArgs(FooSearchArgs searchArgs);
}

所以,一些初始点:

  • 所有三个都返回 Foo 对象的集合
  • 这三个都采用一个参数
  • 规范方法限制了对特定要求的访问
  • 谓词方法基本没有限制
  • 搜索 args 方法限制对特定要求的访问

现在,进入实现:

public ICollection<Foo> GetFoosBySpecification(Specification<Foo> specification)
{
    return fooDataRepository
            .Find()
            .Where(f => specification.IsSatisfiedBy(f))
            .ToList();
}

public ICollection<Foo> GetFooByPredicate(Func<Foo, bool> predicate)
{
    return fooDataRepository
            .Find()
            .Where(predicate)
            .ToList();
}

public ICollection<Foo> GetFooBySearchArgs(FooSearchArgs searchArgs)
{
    return fooDataRepository
            .Find()
            .WhereMeetsSearchCriteria(searchArgs)
            .ToList();
}

实施要点:

  • 这三者的实现都极其简单(一行代码)
  • 在外部实施过滤的规范和搜索参数。
  • Search args 方法只是使用 IEnumerable 扩展方法来检查 args

那么,话虽如此,在什么情况下您会使用上述 3 种技术中的一种?

我对规范模式的看法:

  • 很好,因为它将业务/域需求隔离到可重用的组件中
  • 非常容易阅读,让代码说英语
  • 涉及相当多的代码(接口、抽象类)。如果我要使用它,我会将抽象放在一个通用程序集中(所以我的解决方案中没有一堆静态文件)。
  • 只需更改规范而不是服务层即可轻松更改需求。
  • 领域逻辑的最高可测试性(规范)

我对扩展方法(管道和过滤器)的看法:

  • 逻辑上的“重量级”,但仍然具有相同的简单性。
  • 将查询逻辑从服务层隔离到静态方法
  • 仍然需要排序的“反射”(检查提供的搜索参数并构建查询)
  • 允许您首先对架构(存储库、服务层)进行编码,而无需考虑特定的业务需求(这在某些场景中很方便)

我对谓词方法的看法:

  • 可用于需要对查询进行粗粒度控制的情况。
  • 适用于规范可能过度的小型项目

我最后的思考逻辑是,如果您正在开发一个复杂的业务应用程序,其中业务需求预先知道但可能会随着时间而改变,那么我会使用规范模式。

但是对于一个“启动”的应用程序,即需求会随着时间的推移而发展,并且有多种方法可以在没有复杂验证的情况下检索数据,我会使用 Pipes 和 Filters 方法。

你的想法是什么?你们中的任何人在使用上述任何方法时遇到过问题吗?有什么建议吗?

即将开始一个新项目,因此这些类型的考虑因素至关重要。

感谢您的帮助。

编辑以澄清规范模式

这是规范模式的相同用法。

Specification<Foo> someSpec; // Specification is an abstract class, implementing ISpecification<TEntity> members (And, Or, Not, IsSatisfiedBy).
someSpec = new AllFoosMustHaveABarSpecification(); // Simple class which inherits from Specification<Foo> class, overriding abstract method "IsSatisfiedBy" - which provides the actual business logic.    
ICollection<Foo> foos = fooDataService.GetFoosBySpecification(someSpec);

【问题讨论】:

  • 不完全清楚Specification&lt;Foo&gt;Func&lt;Foo, bool&gt; 有何不同。
  • @Gabe - 我不想发布关于规范模式是如何实现的代码,因为 a) 它有很多代码(4 个类)和 b) 我提供的链接提供了实现细节。
  • 我阅读了链接;我只是看不出语义上的差异。
  • @Gabe,那么您显然不了解规范模式。 Func 表示您传入一个lamba谓词 - 即 .GetFoosByPredicate(f => f.Bar = "FooBar");规范是满足属性封装在方法(IsSatisfiedBy)中的特定对象。
  • 所以你可以写.GetFoosByPredicate(f =&gt; f.Bar = "FooBar").GetFoosBySpecification(new Specification&lt;Foo&gt;(f =&gt; f.Bar = "FooBar"))?我看不出这能带给你什么。一种方式是否比另一种方式更具表现力?一个更容易使用吗?快点?提供更好的抽象?

标签: design-patterns architecture extension-methods specification-pattern pipes-filters


【解决方案1】:

根据我的小经验:

  1. 用户需求总是在变化,我不知道为什么我的老板总是允许这种变化发生。所以 +1 规范
  2. 这里的程序员更像是“体力劳动者”而不是“知识工作者”。你知道.. 那个整天打字的人。通过使用规范,我可以确保每个人都“输入”。这得到了我项目性质的支持。为了相同的目的,它需要许多不同的实现。不要问我为什么。
  3. 使用可提供最高模块化和灵活性以及可测试性的设计模式。这是一个小故事。
    在一个晴朗的日子里,我的伙伴告诉我,他写了一个类,使我们能够计算 32 种计算 X 的方式。他已经实现了所有这些。 Hoho,我认为那是一个英雄式的编程。他花了几个星期在半夜这样做。他认为自己是一个优秀的程序员,所以他坚持大家都使用他的杰作。
    当时我们不关心单元测试,所以我们使用了他的杰作。那时发生了什么?代码一直崩溃。嗯,从那时起,我意识到单元测试和模块化的重要性。

【讨论】:

    【解决方案2】:

    好吧,首先我会编写 Predicate 方法,即使它只是用作其他两个的私有实现细节:

    private ICollection<Foo> GetFoosBySpecification(Specification<Foo> spec) 
    { 
        return GetFooByPredicate(f => spec.IsSatisfiedBy(f));
    } 
    

    搜索参数函数将是类似的单线。

    除此之外,我真的不能抽象地说什么。我必须更多地了解数据结构才能决定寻找它们的最佳方式。

    【讨论】:

      猜你喜欢
      • 2013-03-09
      • 1970-01-01
      • 2011-03-18
      • 2015-06-17
      • 2021-06-17
      • 2021-08-25
      • 1970-01-01
      • 2013-08-22
      • 2020-08-05
      相关资源
      最近更新 更多