Is the specification template meaningless?

I'm just wondering if the spec template is pointless, given the following example:

Suppose you want to check if a customer has sufficient balance in his account, you must create a specification, for example:

new CustomerHasEnoughMoneyInTheirAccount().IsSatisfiedBy(customer) 

However, I am interested, I can achieve the same "advantages" of the specification template (for example, only to change business rules in place) using the getter property in the Customer class as follows:

 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 getter property to port business logic and creating the Specification class?

Thank you all in advance!

+8
c # domain-driven-design specification-pattern
source share
4 answers

Since the specification class allows you to create new criteria without changing the objects themselves.

+7
source share

In a general sense, a Specification object is just a predicate wrapped in an object. If a predicate is used very often with a class, it may make sense to "Move the method"> the predicate to the class to which it belongs.

This template really comes to life when you create something more complex, like this:

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

and transfer it or serialize; this may make even more sense when you provide some kind of β€œspecs” user interface.

However, C # provides more idiomatic ways of expressing things like 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 played with some experimental code that implements specifications in terms of Expression s, with very simple static design 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; } } } 

However, this is a whole template download that does not add value! These Expression only look at public properties, so you can just use the plain old lambda! Now, if one of these specifications needs access to a non-public state, we really need a construction method with access to a non-public state. I will 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 composition from 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 believe that part of the flaw in this can lead to complex Expression trees. For example, build this:

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

creates an Expression tree that looks like this. (These are slightly formatted versions of what ToString() returns when called in Expression - a note that you won’t be able to see the structure of an expression at all if you have only a simple delegate! A few notes: a DisplayClass is a class generated by the compiler, which contains the local variables captured in the closure to solve the rising wave problem , and the resettable Expression uses a single = sign to represent equality comparison, not C # 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)))) 

Dirty! Many calls to immediate lambdas and stored lock references created in build methods. Substituting links to the closure with their captured values ​​and Ξ²-reduction nested lambdas (I also Ξ±-converted all parameter names to unique generated characters as an intermediate step to simplify Ξ²-reduction), 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, fairly printed, edited, passed to LINQ interfaces that understand Expression trees (such as those provided by EF) or whatever you have.

On the side of the note, I built a silly little micro-test and actually found that removing lock references had a significant impact on the speed of evaluating the Expression example when compiling the delegate - this reduced the evaluation time by almost half (!), From 134.1 ns to 70, 5 ns per call on the car where I am sitting in front of me. On the other hand, Ξ²-reduction did not reveal any distinguishable differences, possibly because compilation does this anyway. In any case, I doubt that the usual set of specification classes can achieve such an evaluation speed for a set of four conditions; if such a regular set of classes had to be built for other reasons, such as the convenience of a code-builder-UI, I think it would be desirable for the set of classes to create Expression rather than evaluate directly, but first think about whether you need a template in general C # - I've seen too much code with an overdose of specs.

+9
source share

See zerkms answer, plus: the specification can also work with abstract types, such as interfaces, or as general, which makes it applicable to the entire range of objects.

Or the check that needs to be done for the client may depend on the context. For example, a customer object may not be valid for a payment role system, but valid for storing it in a database in the middle of the process for further processing when the user logs in again. Using the specifications, you can create groups of related checks in a centralized location and disable the entire set depending on the context. In this situation, you can combine it with a factory pattern, for example.

+3
source share

Yes, that is pointless.

The Wikipedia article criticizes this pattern in detail. But I see that the biggest criticism is only the effect of the internal platform . Why reinvent the AND operator? Again, read the Wikipedia article for more complete criticism.

Henry, you're right to assume that Property Get is superior. Why should you avoid a simpler, understandable concept of OO for an unclear "picture" that in its concept does not answer your question? This is an idea, but a bad one. This is an anti-pattern, a pattern that works against you, encoder.

You asked what the difference is, but a more useful question: when should the specification template be used?

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

First, you must understand that this is not a general theory, it is only a concrete example; with specific class modeling {Specification, AndSpecification, ...}. Given the broader theory of domain-based, you can opt out of this template and still have excellent parameters that everyone is familiar with: for example, well-named objects / methods / properties for modeling the language and domain logic.

Jeffrey said:

a specification object is just a predicate wrapped in an object

This is true for domain-driven, but not for a specification template. Jeffrey comprehensively describes a situation where you might need to dynamically create an IQueryable expression, so it can be executed efficiently in the SQL Database. Its final conclusion is that you cannot do this with the specification template as it prescribed. Jeffrey IQueryable expression trees are one of the alternative ways to isolate logical rules and apply them in different composites. As you can see from his sample code, this is verbose and very inconvenient to work with. I can not imagine a single situation that would require such dynamic compositions. And if necessary, there are many other methods available that are simpler.

We all know that you should optimize performance last. Trying to achieve Bleeding here with IQueryable expression trees is a trap. Instead, start with the best tools, first with the simple and concise Getter property. Then check, evaluate and prioritize what kind of work remains.

I have yet to experience a situation where this specification template is needed / better. When I come across suspected situations, I have listed them here and refuted them. If I encounter a good situation, I will revise this answer in a new section.

RE: zerkms answer

Since the specification class allows you to create new [sic] criteria without changing the objects themselves.

C # is already suitable for situations like this:

  • Inheritance (in general), in which you then extend the inherited class (this is good if you do not own the namespace / library where the class comes from)
  • Method override in inheritance
  • Partially - great when you have data model classes. You can add [NotStored] properties nearby and enjoy all the bliss of accessing the information you need directly from the property. When you click '.' IntelliSense tells you which members are available.
  • Extension methods are excellent when Inheritance is not practical (the architecture does not support it), or if the parent class is sealed.

In projects that I undertake, I come across anti-patterns such as Specification Pattern, etc. They are often located in a separate project / library (excessive fragmentation of projects is another terrible practice), and everyone is too afraid to expand objects.

+2
source share

Source: https://habr.com/ru/post/650145/


All Articles