Saturday 10 March 2007

Fluent Specification Part 2: Becoming more fluent

Last post we went through combining the two patterns Fluent Interface and Specification to make a Fluent Specification and ended up with something like this:
customer.Ask(IsCustomer.OldEnoughToBuyGoods(goods))
OK this all looks pretty good. Now I can ask my customer any question I like but I can only ask them one at a time so I would still have to do:
customer.Ask(IsCustomer.OldEnoughToBuyGoods(goods)) && customer.Ask(IsCustomer.AbleToAffordGoods(goods)) 
Wouldn't it be better if we could extend our fluent interface to make it more, well, fluent?
customer.Ask(IsCustomer.OldEnoughToBuyGoods(goods)).And(IsCustomer.AbleToAffordGoods(goods))
This starts looking more like predicates now (where you can join logical rules together easily).
To achieve this we need a new object called a Question:
public class CustomerQuestion
{
readonly bool answer;
readonly Customer customer;

public CustomerQuestion(bool answer, Customer customer)
{
this.answer = answer;
this.customer = customer;
}
public CustomerQuestion And(ISpecification spec)
{
return new CustomerQuestion(answer && spec.IsSatisfiedBy(customer), customer);
}
public bool Answer
{
get{return answer;}
}
}
And we need to update our Customer Ask() method to look like this:
public CustomerQuestion Ask(ISpecification spec)
{
return new CustomerQuestion(spec.IsSatisfiedBy(this), this);
}
Now we can link Questions together easily and by simply extending the Question we can add methods for Or, OrNot and AndNot allowing us to make nice little statements like:
customer.Ask(IsCustomer.OldEnoughToBuyGoods(goods)).AndNot(IsCustomer.GivingMeFakeId())
There are a few advantages to the Fluent Specification which I’d just like to go over before wrapping up this post; they are:

  • It is easy to join specifications together.
  • Clients can easily apply specifications to objects.
  • It makes the codes intent clearer.
  • Common specifications can be gathered together in a Factory making a clear API of business rules.
  • Unlike some Fluent Interfaces it doesn’t break Query and Command.

Next post I’d like to go over extending the Fluent Specification even further.

2 comments:

Maruis Marais said...

Nice example of combining two patterns. I really enjoyed your posts . Interestingly, I haven't yet seen any examples of the incarnations of Subsumption and Partially Fulfilled Specification Pattern.

Colin Jack said...

Recreated your example to see what I thought, it is definitely quite nice stuff but we've gone from having these classes...

1. Customer
2. Specifications

...to having these classes...

1. Customer
2. IsCustomer
3. Specifications
4. CustomerQuestion

And at the end of the day what thats buying you is the ability to do this:

bool canBuy = customer.Ask(IsCustomer.OldEnoughToBuyGoods(goods)).And(IsCustomer.AbleToAffordGoods(goods)).Answer;

My question is whether it is worth the effort, as compared to a more general specification pattern implementation:

bool canBuy = new LegalAgeSpecification(goods).And(new AvailableMoneySpecification(goods)).IsSatisfiedBy(customer);

About Me

My photo
West Malling, Kent, United Kingdom
I am a ThoughtWorker and general Memeologist living in the UK. I have worked in IT since 2000 on many projects from public facing websites in media and e-commerce to rich-client banking applications and corporate intranets. I am passionate and committed to making IT a better world.