문제

I'm just wondering if Specification pattern is pointless, given following example:

Say you want to check if a Customer has enough balance in his/her account, you would create a specification something like:

new CustomerHasEnoughMoneyInTheirAccount().IsSatisfiedBy(customer)

However, what I'm wondering is I can achieve the same "benefits" of Specification pattern (such as only needing to change the business rules in on place) by using Property getter in the Customer class like this:

public class Customer
{

     public double Balance;

     public bool HasEnoughMoney
     {
          get { return this.Balance > 0; }
     }
}

From client code:

customer.HasEnoughMoney

So my question really is; what is the difference between using the property getter to wrap the business logic, and creating Specification class?

Thank you all in advance!

도움이 되었습니까?

해결책

Because with the specification class you can create new criterias without modification of the objects themselves.

다른 팁

In the general sense, a Specification object is just a predicate wrapped up in an object. If a predicate is very commonly used with a class, it might make sense to Move Method the predicate into the class it applies to.

This pattern really comes into its own when you're building up something more complicated like this:

var spec = new All(new CustomerHasFunds(500.00m),
                   new CustomerAccountAgeAtLeast(TimeSpan.FromDays(180)),
                   new CustomerLocatedInState("NY"));

and passing it around or serializing it; it can make even more sense when you're providing some sort of "specification builder" UI.

That said, C# provides more idiomatic ways to express these sorts of things, such as extension methods and LINQ:

var cutoffDate = DateTime.UtcNow - TimeSpan.FromDays(180); // captured
Expression<Func<Customer, bool>> filter =
    cust => (cust.AvailableFunds >= 500.00m &&
             cust.AccountOpenDateTime >= cutoffDate &&
             cust.Address.State == "NY");

I've been playing around with some experimental code that implements Specifications in terms of Expressions, with very simple static builder methods.

public partial class Customer
{
    public static partial class Specification
    {
        public static Expression<Func<Customer, bool>> HasFunds(decimal amount)
        {
            return c => c.AvailableFunds >= amount;
        }

        public static Expression<Func<Customer, bool>> AccountAgedAtLeast(TimeSpan age)
        {
            return c => c.AccountOpenDateTime <= DateTime.UtcNow - age;
        }


        public static Expression<Func<Customer, bool>> LocatedInState(string state)
        {
            return c => c.Address.State == state;
        }
    }
}

That said, this is a whole load of boilerplate that doesn't add value! These Expressions only look at public properties, so one could just as easily use a plain old lambda! Now, if one of these Specifications needs to access non-public state, we really do need a builder method with access to non-public state. I'll use lastCreditScore as an example here.

public partial class Customer
{
    private int lastCreditScore;

    public static partial class Specification
    { 
        public static Expression<Func<Customer, bool>> LastCreditScoreAtLeast(int score)
        {
            return c => c.lastCreditScore >= score;
        }
    }
}

We also need a way to make a composite of these Specifications - in this case, a composite that requires all children to be true:

public static partial class Specification
{
    public static Expression<Func<T, bool>> All<T>(params Expression<Func<T, bool>>[] tail)
    {
        if (tail == null || tail.Length == 0) return _0 => true;
        var param = Expression.Parameter(typeof(T), "_0");
        var body = tail.Reverse()
            .Skip(1)
            .Aggregate((Expression)Expression.Invoke(tail.Last(), param),
                       (current, item) =>
                           Expression.AndAlso(Expression.Invoke(item, param),
                                              current));

        return Expression.Lambda<Func<T, bool>>(body, param);
    }
}

I guess part of the downside to this is it can result in complicated Expression trees. For example, constructing this:

 var spec = Specification.All(Customer.Specification.HasFunds(500.00m),
                              Customer.Specification.AccountAgedAtLeast(TimeSpan.FromDays(180)),
                              Customer.Specification.LocatedInState("NY"),
                              Customer.Specification.LastCreditScoreAtLeast(667));

produces an Expression tree that looks like this. (These are slightly formatted versions of what ToString() returns when called on the Expression - note that you wouldn't be able to see the structure of the expression at all if you had only a simple delegate! A couple of notes: a DisplayClass is a compiler-generated class that holds local variables captured in a closure, to deal with the upwards funarg problem; and the dumped Expression uses a single = sign to represent equality comparison, rather than C#'s typical ==.)

_0 => (Invoke(c => (c.AvailableFunds >= value(ExpressionExperiment.Customer+Specification+<>c__DisplayClass0).amount),_0)
       && (Invoke(c => (c.AccountOpenDateTime <= (DateTime.UtcNow - value(ExpressionExperiment.Customer+Specification+<>c__DisplayClass2).age)),_0) 
           && (Invoke(c => (c.Address.State = value(ExpressionExperiment.Customer+Specification+<>c__DisplayClass4).state),_0)
               && Invoke(c => (c.lastCreditScore >= value(ExpressionExperiment.Customer+Specification+<>c__DisplayClass6).score),_0))))

Messy! Lots of invocation of immediate lambdas and retained references to the closures created in the builder methods. By substituting closure references with their captured values and β-reducing the nested lambdas (I also α-converted all parameter names to unique generated symbols as an intermediate step to simplify β-reduction), a much simpler Expression tree results:

_0 => ((_0.AvailableFunds >= 500.00)
       && ((_0.AccountOpenDateTime <= (DateTime.UtcNow - 180.00:00:00))
           && ((_0.Address.State = "NY")
               && (_0.lastCreditScore >= 667))))

These Expression trees can then be further combined, compiled into delegates, pretty-printed, edited, passed to LINQ interfaces that understand Expression trees (such as those provided by EF), or what have you.

On a side note, I built a silly little micro-benchmark and actually discovered that closure reference elimination had a remarkable performance impact on the speed of evaluation of the example Expression when compiled to a delegate - it cut the evaluation time nearly in half(!), from 134.1ns to 70.5ns per call on the machine I happen to be sitting in front of. On the other hand, β-reduction made no detectable difference, perhaps because compilation does that anyway. In any case, I doubt a conventional Specification class set could reach that kind of evaluation speed for a composite of four conditions; if such a conventional class set had to be built for other reasons such as the convenience of builder-UI code, I think it would be advisable to have the class set produce an Expression rather than directly evaluate, but first consider whether you need the pattern at all in C# - I've seen way too much Specification-overdosed code.

Yes, it is pointless.

The Wikipedia article criticises this pattern at length. But I see the biggest criticism being solely the Inner-Platform Effect. Why re-invent the AND operator? Again, read the Wikipedia article for the fuller criticism.

Henry, you are correct to assume the Property Get is superior. Why eschew a simpler, well-understood OO concept, for an obscure "pattern" which in its conception, doesn't answer your very question? It's an idea, but a bad one. It's an anti-pattern, a pattern that works against you, the coder.

You have asked what is the difference, but a more useful question is, when should a Specification Pattern be used?

Never use this pattern, is my general rule for this pattern.

First, you should realise this isn't a generic theory, it's only a specific pattern; with a specific modeling of classes { Specification, AndSpecification, ...}. With the broader domain-driven theory in mind, you can abandon this pattern, and still have superior options that everyone is familiar with: for instance, well-named objects/methods/properties to model domain language and logic.

Jeffrey said:

a Specification object is just a predicate wrapped up in an object

That's true of domain-driven, but not the Specification Pattern specifically. Jeffrey, comprehensively describes a situation where one may want to dynamically build up an IQueryable expression, so it can efficiently execute on the data store (SQL Database). His final conclusion, is that you can't do that with the Specification Pattern as it's prescribed. Jeffrey's IQueryable expression trees are one alternative way to isolate logical rules and apply them in different composites. As you can see from his example code, it's verbose and very awkward to work with. I can't imagine any situation which would require such dynamic composites either. And if needed, there are many other techniques available which are simpler.

We all know you should optimise performance last. Attempting here to achieve Bleeding edge with IQueryable expression trees, is a trap. Instead, start with the best tools, a simple and terse Property Getter first. Then test, evaluate and prioritise what work remains.

I am yet to experience a situation where this Specification Pattern is necessary/better. As I do come across supposed situations, I'll list them here and rebut them. If I come across a good situation, I'll revise this answer with a new section.

RE: zerkms answer

Because with the specification class you can create new criterias [sic] without modification of the objects themselves.

C# already caters for such situations:

  • Inheritance (in General), where you then extend the inherited class (this is good when you don't own the namespace/library from whence the class comes)
  • Method Overriding in Inheritence
  • Partial - great when you have data-model classes. You can add [NotStored] properties alongside, and enjoy all the bliss of accessing the information you need directly off the object. When you press '.' IntelliSense tells you what members are available.
  • Extension Methods are great when Inheritance is not practical ( architecture doesn't support it ), or if the parent class is sealed.

In projects I take over from, I encounter anti-patterns like Specification Pattern, and more. They're often in a separate Project/Library (over-fragmentation of Projects is another terrible practice) and everyone is too scared to extend objects.

See zerkms answer, plus: a specification can also work on abstract types like interfaces or as a generic making it applicable to a whole range of objects.

Or the check that needs to be done on customer might depend on the context. For example a customer object might not be valid for the pay role system yet, but valid for saving it in the database in the middle of a process for further processing when the user logs in again. With specifications you can build groups of related checks in a centralised location and switch out the entire set depending on context. In this situation you'd combine it with a factory pattern for example.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top