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
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.