【问题标题】:Using the Specification Pattern使用规范模式
【发布时间】:2010-03-24 09:15:22
【问题描述】:

与任何设计模式一样,Specification Pattern 是一个很棒的概念,但容易被急切的架构师/开发人员过度使用。

我即将开始开发一个新应用程序(.NET 和 C#),我非常喜欢规范模式的概念,并渴望充分利用它。然而,在我全力以赴之前,我真的很想知道是否有人可以分享在开发应用程序时使用Specification Pattern 时遇到的痛点。

理想情况下,我想看看其他人是否有问题

  • 根据规范模式编写单元测试
  • 确定规范应位于哪一层(存储库、服务、域等)
  • 只要一个简单的 if 语句就能完成工作,就可以在任何地方使用它
  • 等等?

提前致谢

【问题讨论】:

  • 那篇文章发表于 2006 年。既然我们已经在 C# 中使用了 lambda,那么这种模式真的有用吗?
  • 我永远看不到使用它的任何用途/好处。
  • @DavidHedlund Lambda 不能替代规范。您可以通过 Lambda 实现它们,但它们应该是可重用的。在整个应用程序中使用相同的 lambda 是代码重复。规范删除了重复,并用领域术语描述了查询。

标签: c# specification-pattern


【解决方案1】:

我不相信 LINQ 是规范模式的替代品。请记住,规范最好用于封装业务逻辑。因此,如果我的业务需求之一是让我获得所有重要客户的多项功能,我的 Linq 语句可能如下所示:

var valuedCustomers = Customers.Where(c => c.Orders.Count > 15 && c.Active).ToList();

我可以在整个应用程序中输入此语句,但如果我想附加客户必须在某个日期之前加入的规则怎么办?好吧,现在我必须检查我的应用程序并更改 Linq。或者,如果我的对象图发生了变化(尽管这不应该是使用该模式的唯一原因)。相反,我可以做这样的事情

var valuedCustomers = Customers.Where(new ValuedCustomerRule.IsSatisfied()).ToList();

我同意该模式已被过度使用,但它对于显示在整个系统中的业务规则非常有帮助,因此当这些规则发生变化时您不会受到影响。对我来说,这类似于在您的应用程序代码中保留 SQL 查询。

【讨论】:

  • 为什么不:var valuedcostumers = Customer.Where(c => c.IsValued()).ToList();
  • 我同意@Wix。我对 LINQ 与 Specification 比较的看法:stackoverflow.com/a/34637613/890890
【解决方案2】:

正如 David 在他的评论中指出的那样,规范的许多有用之处现在可以通过 LINQ 之类的工具更简洁地实现。

您可以即时创建任意规范,而不是新的规范类型: GetCustomers().Where(customer => customer.IsActive && customer.City == "Oakland");

然而,这并不能完全替代规范,原因如下:

  1. 返回所有客户后,在消费类中进行排序/过滤。如果您正在处理内存中对象以外的任何内容,则这是次优的(LINQ-to-SQL 等是例外,因为它们编译和优化查询并在服务器/远程端执行它们,只返回所需的结果)。
  2. 如果您公开集合并将规范留给 LINQ 查询,那么您的 API 对任何查询都是开放的。如果您想限制可以检索的内容或数量,则需要一组用于查询的规范。

当然没有必要到处使用它,而且很有可能你根本不需要它;我不知道你的域或你在做什么。

我认为最好的建议是不要去使用它。您会看到什么时候需要这样做,很可能是在您开始编写一个看起来像您链接到的文章中的第一个示例的类时。

只需将其保存在您的思维模式存储库中,以便您在需要时获得;如果你从不使用它,那只意味着你不需要它,没关系。

层的选择取决于规格和用途的性质。在许多情况下,它们是支持服务层的助手,但在某些情况下,它们封装了域逻辑。

至于单元测试,请记住您的规范是单位或包含单位。不要测试接受具有所有可能的规范组合的规范的方法;测试规范本身以确保它们的行为符合预期,然后您可以放心地在许多方法和类中重用相同的规范。

希望对您有所帮助。

【讨论】:

  • @Jay 非常感谢您富有洞察力的回复。干杯凯恩
  • 没问题。我还应该提到,使用 lambdas 允许比“客户端” LINQ 的另一种规范形式:将匿名委托作为方法参数传递。 GetCustomers(customer => customer.IsActive && customer.City == "Oakland");
  • @Jay 规范是为了封装业务含义,避免代码重复。我并不是说它们应该在任何地方都使用,但是在你的代码中传播 lambdas 并不能替代 Specifications 应该给你买的东西。
【解决方案3】:

更新的答案: 我同意 Wix,LINQ 不能替代规范模式。我没有足够的声誉来为 Wix 答案添加评论,我只是作为答案回复。

根据this paper 规范主要用于将域对象与语句进行匹配。虽然规范在与域对象匹配时返回真/假,但这并不意味着规范是 建议在您的代码中封装任何布尔条件。

我将继续 Wix 的回答。如果您定义了 ValuedCustomer 规范,那么您可以使用不同的规则:

   var valuedCustomer = new ValuedCustomerSpecification();
   //1. You can use this statement to check if a customer is valued or not in your domain
   Customer customer = ....
   if(valuedCustomer.IsSatisfiedBy(customer))
   
   //2. You can use it to get just valued customers from repository, 
   var valuedCustomers = repository.Get(valuedCustomer);
   
   //3. You can combine it with other specifications to create composite specifications. Following syntax can vary with implementation
   var seniorValuedCustomer = valuedCustomer.And(seniorCustomer)

我对@9​​87654322@ 的理解是,只要有需要并且您的应用 UI 允许,用户可以与规范进行交互以实现他们的目标。

上述论文还提到了何时不使用规范模式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-13
    • 2016-12-14
    • 2010-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多