views:

162

answers:

3

I know that the Specification pattern describes how to use a hierarchy of classes implementing ISpecification<T> to evaluate if a candidate object of type T matches a certain specification (= satisfies a business rule).

My problem : the business rule I want to implement needs to evaluate several objects (for example, a Customer and a Contract).

My double question :

  • Are there typical adaptations of the Specification patterns to achieve this ? I can only think of removing the implementation of ISpecification<T> by my specification class, and taking as many parameters as I want in the isSatisfiedBy() method. But by doing this, I lose the ability to combine this specification with others.

  • Does this problem reveal a flaw in my design ? (i.e. what I need to evaluate using a Customer and a Contract should be evaluated on another object, like a Subscription, which could contain all the necessary info) ?

+1  A: 

Your problem is that your specification interface is using a generic type parameter, which prevents it from being used for combining evaluation logic across different specializations (Customer,Contract) because ISpecification<Customer> is in fact a different interface than ISpecification<Contract>. You could use Jeff's approach above, which gets rid of the type parameter and passes everything in as a base type (Object). Depending on what language you are using, you may also be able to pull things up a level and combine specifications with boolean logic using delegates. C# Example (not particularly useful as written, but might give you some ideas for a framework):

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 */
}
jlew
Good point about generics - I overlooked the actual code in the question! D:
Jeff Sternal
+2  A: 

In that case (depending on what the specification precisely should do, I would use one of the objects as specification subject and the other(s) as parameter.

Example:

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

    public Contract SelectedContract { get; private set; }

    public bool IsSatisfiedBy(Customer subject)
    {
        return false;
    }
}
Paco
A: 

I don't know if I understood your question.

If you are using the same specification for both Customer and Contract, this means that you can send the same messages to both of them. This could be solved by making them both to implement an interface, and use this interface as the T type. I don't know if this makes sense in your domain.

Sorry if this is not an answer to your question.

bloparod