SPECIFICATION

Generally saying Specification is a predicate that determines if an object does or does not satisfy some criteria. By using Specifications you could easily recombine business logic together using boolean logic.

Have you ever thought that bool TryParse(string s, out int result) could be seen as some pattern? Yes we could talk about this method like about specification of integer represented in string, and this is validation usage. This pattern also could be used not only for Validation, but also for Queuring and Building purposes.

Let us imagine that we need to validate if some Patient is eligible for drugs procedures at home provided by nurse.

So this we will need two specifications. If they are both satisfied we could say that this patient is eligible for drugs procedures at home.
 
First specification

    public class EligibleForDrugs : ISpecification
    {
        public bool IsSatisfiedBy(Patient patient)
        {
            return patient.IsActive && patient.HasPayer;
        }

    }
 
Second specification

    public class EligibleForNurseVisit : ISpecification
    {
        public bool IsSatisfiedBy(Patient patient)
        {
            return patient.IsActive && patient.IsAtHome;
        }
    }

As you guess ISpecification interface looks like:

    internal interface ISpecification
    {
        bool IsSatisfiedBy(Patient patient);
    }

Usage could look like:

        public List<Patient> FetchPatientsForVisitWithDrugs(List<Patient> patients)
        {
            var eligiblePatients = new List<Patient>();
            ISpecification drugsSpec = new ElegibleForDrugs();
            ISpecification nurseVisit = new ElegibleForNurseVisit();
            foreach (var patient in patients)
            {
                if(drugsSpec.IsSatisfiedBy(patient) && nurseVisit.IsSatisfiedBy(patient))
                {
                    eligiblePatients.Add(patient);
                }
            }
            return eligiblePatients;
        }

You could say that we can put all verification in our method FetchPatientsForVisitWithDrugs. Yes, but this is not right way, because your specifications could be used in different locations and also if see this from DDD perspective you always need to bring concept things into the light.

Another question: Don’t you see this systax
if(drugsSpec.IsSatisfiedBy(patient) && nurseVisit.IsSatisfiedBy(patient))
to be boring systax? Yes, especially if you have many specifications.

Let us improve our design.

First we will add some methods to our interface like here:

    public interface ISpecification
    {
        bool IsSatisfiedBy(Patient patient);
        ISpecification And(ISpecification secondSpec);
        ISpecification Or(ISpecification secondSpec);
        ISpecification Not(ISpecification secondSpec);

    }

And also will add CompositeSpecification which will be abstract  base class for our two existing.

    public abstract class CompositeSpecification : ISpecification
    {
        public abstract bool IsSatisfiedBy(Patient patient);
        public ISpecification And(ISpecification secondSpec)
        {
            return new AndSpecification(this, secondSpec);
        }
        public ISpecification Or(ISpecification secondSpec)
        {
            return new OrSpecification(this, secondSpec);
        }
        public ISpecification Not(ISpecification secondSpec)
        {
            return new NotSpecification(secondSpec);
        }
    }
Classes returned by different method of this new CompositeSpecification are used to combine few specifications in order to build new complicated one. They could look simple like this AndSpecification class:
    public class AndSpecification : CompositeSpecification
    {
        private ISpecification firstOne;
        private ISpecification secondOne;
        public AndSpecification(ISpecification firstSpec, ISpecification secondSpec)
        {
            firstOne = firstSpec;
            secondOne = secondSpec;
        }
        public override bool IsSatisfiedBy(Patient patient)
        {
            return firstOne.IsSatisfiedBy(patient) && secondOne.IsSatisfiedBy(patient);
        }
    }

Let’s move to our existing specifications and how they changed with our new design:

    public class EligibleForDrugs : CompositeSpecification
    {
        public override bool IsSatisfiedBy(Patient patient)
        {
            return patient.IsActive && patient.HasPayer;
        }

    }

This all gives us ability to have build specifications quering in more sweet way.
New Usage

            ISpecification specification = new EligibleForDrugs()
                                           .And(new EligibleForNurseVisit());

Now we work with this new specification as with simple single one: if(specification.IsSatisfiedBy(patient))
With other specifications like OrSpecification and NotSpecification we could build more complicated queries.

For example I will show some possible usage of this in Nhibernate:

        public DetachedCriteria PatietQuery(DetachedCriteria criteria)
        {
            criteria.Add(criteria.EqualTo(“patient.IsActive”, true));
            criteria.Add(
                    Expression.Not(
                        Expression.Or(
                            Expression.Eq(“patient.Type”, PatientStatus.Discharge),
                            Expression.Eq(“patient.Type”, PatientStatus.Death)
                        )
                    )
                );
            return criteria;
        }

Advantages of the Specification:

  • Specification declares requirements to the output but do not expose how those results are reached.
  • Rules are defined explicitely. This means that developer could know what to expect from the specification even without knowledge how that is realized.
  • You get flexible interface, which could be easily enhanced. You also could build your composite specifications for queuring.
  • Another good advantage is possitility to test everything easily. You just define fail, non-fail states of the object and verify by checking boolean result.

If you haven't subsribed yet, you can subsribe below: