Expect.Once.On(object).Method("Name").With(value).Will(Return.Value(returnValue))Compared to the old non-fluent interface of:
mockObject.ExpectAndReturn("Name", returnValue, value);At first it looks like more code but as expectations become more complex and to anyone reading the code the fluent interface is a lot clearer and easier to work with.
The specification pattern comes from Eric Evans and is found in detail in his book Domain Driven Design. The idea of the specification pattern is to break your business rules into small self contained, reusable objects called specifications. The specification has an bool IsSatisfiedBy(object) method. The idea is to give some sort of predicate style to OOP languages. So rather than:
if(customer.Age >= goods.MinimumAge)You replace it with a LegalAgeSpecification which looks like the following:
if(new LegalAgeSpecification(goods).IsSatisfiedBy(customer))Which again, although may look like more stuff going on once you've got a few of these scattered about your class soon becomes clearer and easier to use. The added bonus is specifications can be reused all over the place: for example for validation, searching collections or within other specifications and they keep all of your business rules in the domain where they belong. What you'll soon discover is you will build up a rich set of business rules in your domain you can call upon at will.
So how about we take this a step further, create some synergy between some patterns (which is undoubtably where patterns get their greatest power) and create a Fluent Specification? The idea behind a Fluent Specification is to make your specifications look more fluent. The above code is rather ugly so why can't we do something like this instead:
if(customer.Ask(IsCustomer.OldEnoughToBuyGoods(goods)))Isn't that a lot clearer already? The goods object now flows with the sentance of the interface rather than trying to hide its role by sitting in a constructor. Already we have made our intent clearer.
So how would we write something like this? Well there are three main components to this example:
- A Question (the Ask() method)
- A specification factory (the IsCustomer singleton)
- A specification (the OldEnoughToBuyGoods object returned from the factory)
Normally I would do this TDD and set up a test which looks like the following:
Customer customer = new Customer();
customer.Age = 14;
Goods goods = new Alchohol();
Assert.IsFalse(customer.Ask(IsCustomer.OldEnoughToBuyGoods(goods)))
And of course test IsTrue as well but I don't want to make this post 100 pages long so you'll just have to take my word for the TDD. So the first thing to do (TDD style) would be to write a test for the specification itself and test it. So my specification looks like:
public class LegalAgeSpecification
{
readonly Goods goods;
public LegalAgeSpecification(Goods goods)
{
this.goods= goods;
}
public bool IsSatisfiedBy(Customer customer)
{
return customer.Age >= goods.MinimumAge;
}
}
I want to just draw attention to a few things about this specification: I have been very specific about passing the goods object and through rather than making a more generic OlderThanSpecification. The reason is that I may wish to expand this specification in the future to include more business rules as they come up: for example the business rule may say that for certain goods we must check for ID, so the rule may expand to this:
public bool IsSatisfiedBy(Customer customer)
{
if(goods.RequireId)
{
return customer.Id.DateOfBirth.CurrentAge >= goods.MinimumAge
}
return customer.Age >= goods.MinimumAge;
}
Of course you may wish to factor out that rule into a seperate ValidIdForMinimumAgeSpecifcation and call it within this one.
Does this go against YAGNI (You Ain't Gonna Need It)? No: because you are making the object more specific and more flexible by making a very simple design desicion. To have created a more generic OlderThanSpecification is more YAGNI as there is no requirement for that just yet. You may find that as time goes on you will refactor out the rule into a seperate spec but on first pass keep your objects specific to the business rule and inline with the ubiquitous language.
So now we have a specification and its up and running so how do we make this fluent? Well I'm going to rush forward and dump the Factory and the Question out in one go (though I was a little more structured using TDD).
public class Customer
{
...
public bool Ask(ISpecification spec)
{
return spec.IsSatisfiedBy(this);
}
}
public static class IsCustomer
{
public static ISpecification OldEnoughToBuyGoods(Goods goods)
{
return new LegalAgeSpecification(goods);
}
}
During my TDD to achieve my goal of a fluid and flexible interface I needed some refactoring and created an interface for the specification. Now my old specifaction implements the ISpecifcation interface making the Ask() method polymorphic.
Next post I’ll go through making this even more fluent.
No comments:
Post a Comment