I’d like to make a couple of points about TDD and iterative development before I go on . Using TDD I have managed to get to a point where I’ve solved one problem at a time and using refactoring I have improved upon my original design and without deviating from my original goal. I have managed to roll out my existing functionality and give real value to my clients. Now I am in a good position to use this proven functionality elsewhere. If I had sat down and said “I’m going to create this great framework for specifications” I would probably have got myself in a complete mess and ended up taking longer to make something unwieldy and over-engineered.
Fluent Specification has been very succesfuly with my customer class so I decide to use it with other objects. I need to employ the power of generics. I don’t think I need to go into detail here so basically I now have the following interfaces:
- IQuestionSyntax
- ISpecification
As I was working on another domain I realised that my I realized that I really liked my Fluent Specification with my customer object and so I decided that I should harvest it http://www.martinfowler.com/bliki/HarvestedFramework.html.
The new domain was for a bouncer application which worked through a queue of clubbers and checked if they were allowed in. So I had some nice code which went something like this:
foreach(Clubber clubber in Queue)
{
if(clubber.Ask(IsClubber.OldEnough().AndNot(IsClubber.BreakingDressCode().And(IsClubber.NameDown(guestList))
{
clubber.RefuseEntry();
)
clubber.ComeIn(event);
}
So we rolled out our bouncer application and my client was very impressed. The number of dodgy clubbers getting in went down and everything was good. A few weeks later my client comes back and says he is getting a large number of complaints because my bouncer is just refusing clubbers without telling them why. I bit my tounge about this implementing standard bouncer behaviour as he likes to operate a customer friendly adult evening enterntainment service. So I went back to my code and tried to find a way of making my bouncer give some feedback.
I had to start introducing some new concepts. For a start my Anwser property became a full fledged object with a Feedback property and an IsTrue and IsFalse properties. I then created a IQuestion
public class Question: IQuestion
{
private readonly ISpecificationspec;
private readonly Feedback feedback;
public Question(ISpecificationspec, Feedback feedback)
{
this.spec = spec;
this.feedback = feedback;
}
public Answer Ask(T obj)
{
return new Answer(spec.IsSatisfiedBy(obj), feedback);
}
}
I then had to upgraded my Or/AndSpecification classes to become Or/AndCompositeQuestion
public class OrCompositeQuestionI then walked through my code refactoring in my new classes and adding the Feedback paramater to my Ask method. Now I could do this:: IQuestion
{
IQuestionquestion1;
IQuestionquestion2;
public OrCompositeQuestion(IQuestionquestion1, IQuestion question2)
{
this.question1 = question1;
this.question2 = question2;
}
public Answer Ask(T obj)
{
Answer result1 = question1.Ask(obj);
Answer result2 = question2.Ask(obj);
Feedback feedback = string.Empty;
if(!result1.IsTrue)
{
feedback = feedback.Append("or", result1.Feedback);
}
if(!result2.IsTrue)
{
feedback = feedback.Append("or", result2.Feedback);
}
return new Answer(result1.IsTrue result2.IsTrue, feedback);
}
}
Answer answer = clubber.Ask(IsClubber.OldEnough(), "over eighteens only mate")
.AndNot(IsClubber.BreakingDressCode(), "trainers aren't allowed")
.And(IsClubber.NameDown(guestList), "if you're names not down you're not coming in");
if(answer.IsFalse)
{
clubber.Refuse(answer.Feedback);
}
clubber.ComeIn();
I have left alot of code out there but I think you should get the picture. There is a bit of cleaning up needed to do and maybe a little refactoring on the structure of my classes but still I've got something that works, it passes my tests, I can demo it to the client and make sure he's happy with my new enhanced Bouncer application.
No comments:
Post a Comment