Home > Architecture and Design > Understanding Specification Pattern–Part 1

Understanding Specification Pattern–Part 1

Introduction

I would like to mention couple of terms before getting into details of Specification Pattern. This is simply because some developers may not aware of these. The first is DDD. Domain Driven Design is an approach for developing software by creating model that is closely matching the domain of the core business. The second is SRP (Single Responsibility Principle).It states that object should have only one reason to change. In other words, an should have just one responsibility. The Specification Pattern encapsulates just one piece of logic. It is recommended to read these posts. The Specification Pattern is mostly used in DDD based projects.

Mostly developers implement the business logic in the entity itself. This is not an ideal option because as we put more and more logic in the entity, it becomes unmaintainable.  The Specification Pattern, implemented as an Object, enable entities to split the business logic in its own. The Specification Object typically have an IsSatisfiedBy method that takes a domain object and return a Boolean value.

For example, we have a Product entity, and we need to be able to check if this product is eligible for discount between certain dates.

Implementing Specification Pattern

Let us look at the basic implementation of the pattern.

public interface ISpecification<T>
{
        bool IsStatisfiedBy(T candidate);
}

Let us know implement the discount specification class.

public class EligibleForDiscountSpecification : ISpecification<Product>
  {
      public bool IsStatisfiedBy(Product prod)
      {
          return (DateTime.Now.Date >= prod.SaleStartDate && DateTime.Now.Date <=

                prod.SaleEndDate );
      }
  }

Now you can just pass the Product object to EligibleForDiscountSpecification.IsSatisfiedBy method and this clearly separate the logic from Product entity. Here is how you can use this Specification object:

var spec = new EligibleForDiscountSpecification();

If(spec.IsSatisfiedBy(product))

{

       ….

}

Typically you use this Specification pattern to select items from the collection. For example, if you want to find the list of products currently on sale, you can do like this:

public IEnumerable<Product> GetAllProductsInSale(IList<Products> products,

ISpecification<Product> spec)

{

          foreach(var prod in products)

         {

              if (spec.IsSatisfiedBy(prod))

                         yield return prod;

        }

}

 

Chaining Specifications

We can actually chain specifications to create composite specifications. This is very powerful method to create complex business rules that involve several rules to that could be nested together. Let us now see how to implement this.

public interface ISpecification<T>
{
        bool IsStatisfiedBy(T candidate);

        ISpecification<T> And(ISpecification<T> other);                                                       

        ISpecification<T> Or(ISpecification<T> other);

        ISpecification<T> Not();
}

 

public abstract class CompositeSpecification<T> : ISpecification<T>
    {
        public abstract bool IsSatisfiedBy(T candidate);

        public ISpecification<T> And(ISpecification<T> other)       

       {
            return new AndSpecification<T>(this, other);
        }

        public ISpecification<T> Or(ISpecification<T> other)       

        {
            return new OrSpecification<T>(this, other);
        }

        public ISpecification<T> Not()       

        {
            return new NotSpecification<T>(this);
        }
    }

 

public class AndSpecification<T> : CompositeSpecification<T>
  {
      private readonly ISpecification<T> left;
      private readonly ISpecification<T> right;

      public AndSpecification(ISpecification<T> left, ISpecification<T> right)
      {
          this.left = left;
          this.right = right;
      }

      public override bool IsSatisfiedBy(T candidate)
      {
          return left.IsSatisfiedBy(candidate) && right.IsSatisfiedBy(candidate);
      }
  }

 

public class OrSpecification<T> : CompositeSpecification<T>
    {
        private readonly ISpecification<T> left;
        private readonly ISpecification<T> right;

        public OrSpecification(ISpecification<T> left, ISpecification<T> right)
        {
            this.left = left;
            this.right = right;
        }

        public override bool IsSatisfiedBy(T candidate)
        {
            return left.IsSatisfiedBy(candidate) || right.IsSatisfiedBy(candidate);
        }
    }

 

 public class NotSpecification<T> : CompositeSpecification<T>
    {
        private readonly ISpecification<T> other;

        public NotSpecification(ISpecification<T> other)
        {
            this.other = other;
        }

        public override bool IsSatisfiedBy(T candidate)
        {
            return !other.IsSatisfiedBy(candidate);
        }
    }

Summary

In this post, I have covered the basics of Specification pattern and provided code snippets. Also I have covered the composite specification and how it is very powerful to implement complex business rules. I will cover some advanced topics in Part 2 of the Specification Pattern which covers how to use this specification pattern with EntityFramework, LINQ.

  1. No comments yet.
  1. No trackbacks yet.

Leave a comment