Saturday, 10 March 2007

Fluent Specification Part 3: Giving feedback

There are a few things still left to be done to improve the Fluent Specification.

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 interface and a standard Question class which wrapped ISpecifications and Feedback (note how I've used whole value for Feedback).

public class Question : IQuestion
{
private readonly ISpecification spec;
private readonly Feedback feedback;

public Question(ISpecification spec, 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 : IQuestion classes. I then added feedback logic into the composite questions like so:

public class OrCompositeQuestion : IQuestion
{
IQuestion question1;
IQuestion question2;

public OrCompositeQuestion(IQuestion question1, 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);
}
}
I then walked through my code refactoring in my new classes and adding the Feedback paramater to my Ask method. Now I could do this:
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:

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.