Mar 16, 2010

Specification Pattern with Func<>

Specification is a pattern (Evans/Fowler) that can be used to apply rules for your domain entities:

Create a specification that is able to tell if a candidate object matches some criteria. The specification has a method isSatisfiedBy (anObject) : Boolean that returns "true" if all criteria are met by anObject.

It defines an interface like

public interface ISpecification

{

    bool IsSatisfiedBy<T>(T candidate);

 

    ISpecification And(ISpecification other);

 

    ISpecification Or(ISpecification other);

 

    ISpecification Not();

}

where IsSatisfiedBy() wraps the business logic (not contained in the entity as OO would suggest).

That allows to write business logic code like

price.And(seats).And(slow.Not())

My car specification:

public class CarSpecification : Specifications.CompositeSpecification

{

    public Func<Car, bool> IsSatisfiedFunction { get; set; }

 

    public CarSpecification(Func<Car, bool> isSatisfiedFunction)

    {

        this.IsSatisfiedFunction = isSatisfiedFunction;

    }

 

    public override bool IsSatisfiedBy<T>(T candidate)

    {

        if (!(candidate is Car))

        {

            throw new ArgumentException("must pass Car entity", "candidate");

        }

 

        if (this.IsSatisfiedFunction != null)

        {

            return this.IsSatisfiedFunction(candidate as Car);           

        }

        return false;

    }

}

The whole code:

public partial class MainWindow : Window

{

    private IList<Car> cars;

    private CarSpecification price = new CarSpecification(c => c.Price <= 40000);

    private CarSpecification seats = new CarSpecification(c => c.Seats >= 7);

    private CarSpecification slow = new CarSpecification(c => c.HorsePower < 140);

 

    public MainWindow()

    {

        InitializeComponent();

 

        this.cars = ApplyRules(Cars.Get());

        this.DataContext = this.cars;

    }

 

    private IList<Car> ApplyRules(IList<Car> cars)

    {

        ISpecification myCarSpecification = price.And(seats).And(slow.Not());

        return cars.Where(c => myCarSpecification.IsSatisfiedBy<Car>(c)).ToList();

    }

}

Definitively this simple example could be written in other ways. More importantly you evaluate your new car ….

image

No comments:

Post a Comment