2010-02-24 65 views
2

我知道规范模式描述了如何使用实现ISpecification<T>的类的层次结构来评估类型T的候选对象是否符合特定规范(=满足业务规则)。如何调整规范模式以评估对象的组合?

我的问题:我想实施的业务规则需要评估几个对象(例如,客户和合同)。

我的双重问题:

  • 是否有在规范模式的典型调整来实现这一目标?我只能想到通过我的规范类去除ISpecification<T>的实现,并且在isSatisfiedBy()方法中采用了我想要的参数。但是通过这样做,我失去了将此规范与其他人结合起来的能力。

  • 这个问题揭示了我设计中的缺陷吗? (即,我需要使用客户和合同进行评估的方式应该在另一个对象上进行评估,如订阅,其中可能包含所有必要的信息)?

+0

你能告诉我们你到底想干什么?我不能给出一个很好的答案,只是“评估几个对象” – Paco 2010-02-24 16:52:50

+0

具体而言,我想创建一个规则,告诉我是否需要为我的客户创建一个电子邮件帐户。这条规则需要知道关于我的客户的东西,以及关于他选择的合同的东西。为了达到这个目的,我想创建一个规范类,它公开一个布尔isSatisfiedBy()方法,它将指示我的规则是否被作为参数传递的候选对象验证。 – ONETHRD 2010-02-24 18:07:28

+1

我的回答不清楚,忽略了您的问题的几个方面,并提出了一些无根据的假设(例如,您可以从合同导航到客户)。所以我删除了它。我认为你在第二个问题上碰到了问题:规范应该运行在一个可以访问客户和合同的对象上。 – 2010-02-24 19:13:36

回答

2

你的问题是,你的规范接口使用泛型类型参数,它可以防止它被用来在不同专业(客户,合同),因为ISpecification <客户>实际上是在不同的界面评价逻辑的结合比ISpecification <合同>。您可以使用上面的Jeff的方法,该方法摆脱了类型参数,并将所有内容作为基本类型(Object)传递。根据你使用的是什么语言,你也可以把事情拉上一个层次,并使用代表将布尔规则与布尔逻辑相结合。 C#示例(如写的,但可能给你一个框架的一些想法不是特别有用):

ISpecification<Customer> cust_spec = /*...*/ 
ISpecification<Contract> contract_spec = /*... */ 
bool result = EvalWithAnd(() => cust_spec.IsSatisfiedBy(customer),() => contract_spec.IsSatisfiedBy(contract)); 

public void EvalWithAnd(params Func<bool>[] specs) 
{ 
    foreach(var spec in specs) 
    { 
     if (!spec()) 
      return false; /* If any return false, we can short-circuit */ 
    } 
    return true; /* all delegates returned true */ 
} 
+0

关于泛型的好处 - 我忽略了问题中的实际代码! D: – 2010-02-24 17:27:12

+0

我会选择这种方法。这样,您仍然拥有非常专注的SRP规范,可以在应用程序的其他部分单独使用,并且您正在编写它们以解决此特定问题。 – Dan 2013-06-26 03:47:12

4

在这种情况下(这取决于规范正是应该做的,我会使用对象的规范主题之一和其他(S)作为参数

例:

public class ShouldCreateEmailAccountSpecification : ISpecification<Customer> 
{ 
    public ShouldCreateEmailAccountSpecification(Contract selectedContract) 
    { 
     SelectedContract = selectedContract; 
    } 

    public Contract SelectedContract { get; private set; } 

    public bool IsSatisfiedBy(Customer subject) 
    { 
     return false; 
    } 
} 
0

如果您使用S,我不知道如果我理解你的问题

。客户和合同的ame规范,这意味着您可以将相同的消息发送给他们。这可以通过使它们都实现一个接口并将此接口用作T型来解决。我不知道你的域名是否有意义。

对不起,如果这不是你的问题的答案。

1

Paco的解决方案是将一个对象作为主体,另一个作为使用构造函数注入的参数,但有时会起作用,但如果两个对象都是在规范对象之后构造的,则会使事情变得非常困难。

解决此问题的一个办法是使用参数对象,如在此重构建议中:http://sourcemaking.com/refactoring/introduce-parameter-object

其基本思想是,如果您认为Customer和Contract是代表相关概念的参数,那么您只需创建另一个包含它们的参数对象。

public class ParameterObject 
{ 
    public Customer Customer { get; set; } 
    public Contract Contract { get; set; } 
} 

那么你的通用规范成为该类型:

public class SomeSpecification : ISpecification<ParameterObject> 
{ 
    public bool IsSatisfiedBy(ParameterObject candidate) 
    { 
     return false; 
    } 
}