Specification Pattern

Specifying your business rule at one single place

Posted by Taher Chhabra on Tuesday, August 23, 2011 Tags: Design Pattern   3 minute read

Suppose there is a method as shown below

public List GetExpensiveProducts()
{
    List products = GetAllProducts();
    List expensiveProducts = new List();
    foreach (product p in products)
    {
        if(product.price > 50)
        {
             expensiveProducts.add(p)
        }
    }
    return expensiveProducts;
}

It is a method which returns all expensive products and there are other 10 methods like

GetExpensiveProductsInStock()
GetExpensiveProductsOutOfStock()
GetExpensiveProductInACategory() etc etc etc ..

now suddenly your business analyst says that “we need to change the definition of expensive products, now products having price higher than 100 are expensive products”

So you’ll need to change the code at 10 different places ..isnt it?

The Specification pattern comes to the rescue !!

Now you can write the above method like this

public List GetExpensiveProducts()
{
    List products = GetAllProducts();
    List expensiveProducts = new List();
    ExpensiveProductSpecification spec = new ExpensiveProductSpecification ()
    foreach (product p in products)
    {
         if(spec.IsSatisfiedBy(p))
         {
               expensiveProducts.add(p)
         }
     }
     return expensiveProducts;
}

The abstract base class of the pattern is as follows

public abstract class Specification
{
     public abstract bool IsSatisfiedBy(T obj);
}

to make a concrete specification, we just need to inherit from the above base class

public class ProductPriceGreaterThanSpecification : Specification
{
     public override bool IsSatisfiedBy(Product product)
     {
          return product.Price > 50
     }
}

We can also create composite specifications, For that we’ll need to create a generic Class named CompositeSpecification by deriving the base class as shown below

public abstract class CompositeSpecification : Specification
{
       protected readonly Specification _leftSide;
       protected readonly Specification _rightSide;

       public CompositeSpecification(Specification leftSide, Specification rightSide)
       {
             _leftSide = leftSide;
             _rightSide = rightSide;
        }
}

now lets create concrete implementations of the CompositeSpecification

public class AndSpecification : CompositeSpecification
{

        public AndSpecification(Specification leftSide, Specification rightSide): base(leftSide, rightSide)
        {

        }

        public override bool IsSatisfiedBy(T obj)
        {
            return _leftSide.IsSatisfiedBy(obj) && _rightSide.IsSatisfiedBy(obj);
        }
}





public class OrSpecification : CompositeSpecification
{

         public OrSpecification(Specification leftSide, Specification rightSide): base(leftSide, rightSide)
         {

         }

         public override bool IsSatisfiedBy(T obj)
         {
              return _leftSide.IsSatisfiedBy(obj) || _rightSide.IsSatisfiedBy(obj);
         }

}





public class NotSpecification : CompositeSpecification
{

     public NotSpecification(Specification leftSide, Specification rightSide): base(leftSide, rightSide)
     {

     }

     public override bool IsSatisfiedBy(T obj)
     {
          return _leftSide.IsSatisfiedBy(obj) && !_rightSide.IsSatisfiedBy(obj);
     }
}   

and some methods in our base class as follows

public abstract class Specification
{
       public abstract bool IsSatisfiedBy(T obj);
       public AndSpecification And(Specification specification)
       {
            return new AndSpecification(this, specification);
       }

       public OrSpecification Or(Specification specification)
       {
              return new OrSpecification(this, specification);
       }

       public NotSpecification Not(Specification specification)
       {
            return new NotSpecification(this, specification);
       }
}

now we are ready to use some composite specifications as shown below

public List GetExpensiveProductsInStock()
{

      List products = GetAllProducts();
      List expensiveProducts = new List();
      foreach (product p in products)
      {

          if(new ProductInStockSpecification().And(new ProductPriceGreaterThanSpecification()).IsSatisfiedBy(p);)
          {
               expensiveProducts.add(p)
          }
      }
      return expensiveProducts;
}