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 CustomerQuestionAnd we need to update our Customer Ask() method to look like this:
{
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;}
}
}
public CustomerQuestion Ask(ISpecification spec)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:
{
return new CustomerQuestion(spec.IsSatisfiedBy(this), this);
}
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:
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.
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);
Post a Comment