The Specification Pattern
This post is based on this post, which is a great post in my opinion and since it already exists I didn’t see the need to rewrite the post so I suggest you go read it first.
The stuff that I am going to add are few observations I have and small implementation enhancement that I think helps the implementation to be even more intuitive.
First, a small overview.
Say you have a class User who has a name, country of residence and a birth date.
It makes sense that you have something like:
if (user.CountryOfResidence == Country.Chad || user.CountryOfResidence == Country.Sudan) { // Do something… }
This code performs some special logic for a user that is coming from Africa and from a country that exists in our system. There is a good chance that code such as this condition is duplicated in a UI page and in a business logic service and maybe in some more places throughout the application. The problems with this are:
Unless the developer who wrote the code added documentation that states it checks that a user came from Africa, we can only guess what it means. It might just be a list of countries for all we know… You might say that it can be placed in a local private method like
if (IsUserFromAfrica(user)) { // Do something }
so now it describes what it means but this still doesn’t solve the code duplication problem. What if now Angola is supported as an African country as well? You will still have to go and change all the occurrences of the condition whether it is inline or wrapped in a method.
By using the Specification Pattern the code will be transformed to this:
if (new UserFromAfricaSpecification().IsSatisfiedBy(user)) { // Do something… }
Where UserFromAfricaSpecification is implemented like this:
public class UserFromAfricaSpecification : Specification<User> { public override bool IsSatisfiedBy(User obj) { return obj.CountryOfResidence == Country.Chad || obj.CountryOfResidence == Country.Sudan; } }
The base class Specification
By working with specifications like that, we get documented, reusable and independently testable "libraries" of specifications.
Since conditions usually are business rules, which are always difficult to maintain and manage, the specification pattern comes very handy and this is the one thing I like the most about it.
As I wrote before, this post shows how the code is built. The code sample attached to my post is almost identical to the code there. The difference is mostly in one detail - specification composition can be performed using the & and | operators, and the ! operator is also available. This means that you can write a specification like this:
if ((new UserOlderThanSpecification(18) & (!new UserFromAfricaSpecification())).IsSatisfiedBy(user)) { // Do something… }
Another use of the specification pattern is to define what we are looking for. The signature of the IsSatisfiedBy method matches the Predicate delegate signature and therefore can be used by the ForEach method of Array or List<> when looking for, say, a user who satisfies a certain condition:
List<User> africanUsers = allUsers.FindAll( new UserFromAfricaSpecification().IsSatisfiedBy);
where allUsers is of type List
What I still don’t like about the current implementation is the fact the the new keyword is used to instantiate a specification instance. I have few ideas about that so expect a follow-up.
The example code for this post is here.