Saturday, July 12, 2008
Decorator pattern in Action
Requirement is as simple as that:
Login story: System should log all informational as well as failure messages into text file.
That sounds way simple, right? Even without using any existing logging framework.
All it takes is just one static class.
public static class Log
{
public static void Write(string message)
{
//code to write in text file
}
}
And the usage looks like
Log.Write(“something weird happen here”);
So far so good.
The solution works so far and meets our requirement.
Next, lets consider following some OOPs. Here is what I got from Head First OOA & D.
• Make sure your software does what client want it to do
• Apply OOP to make it maintainable.
• Re-factor to improve any duplication.
We have already satisfied first bullet line. So let’s proceed to second one.
One of the OOP says “Depend upon abstraction”. But in our solution, we are tightly binding client with static class. So let’s instead use interface and push all the concrete code behind it.
Our new implementation looks like this:
public interface ILogger
{
void Write(string message);
}
public class TextFileLogger : ILogger
{
private readonly ITextFileWritter _textFileWritter;
public TextFileLogger(ITextFileWritter textFileWritter)
{
_textFileWritter = textFileWritter;
}
public void Write(string message)
{
_textFileWritter.Write(message);
}
}
If we look at closely here, rather than writing code for IO into logger class, I have pushed them into their own class. Why????? Remember Single Responsibility Principle? Class should have only one reason to change, right? So our logger should be responsible for logging not for writing to text file. This also gives me good opportunity to unit test my logger. To achieve this goal, I have used one of my favorite Inversion of Control techniques called Dependency Injection [This might sounds like weird but I am favoring this approach because it gives me, unit testing benefits plus we have textFilewritter, which can be used anywhere else too].
Now, at this stage, our solution looks great. It perfectly meets client need as well as follows OOP from the maintainability point of view.
It’s been well said for software: The only constant in software is Change. So what if we have requirement coming up later: need to feed all these log messages to xyz MQ, so that somebody can take corrective action on them.
With our new code it should be that difficult as all we have to do is CHANGE TextFileLogger class. Add one line there, to make call to some class which will write into MQ.
Remember though, key here is our ability to reach at this file and CHANGE code in it. We may not be lucky to get access to source file every single time (This is the case, almost every single time when code that we wrote, is shipped as part of some framework). And again its always risky to change somebody else’s code. Now you have to test not only your new functionality but existing one too and make sure you haven’t broke any thing (Isn’t that very common, breaking existing code while making change. People working on maintenance project, knows this better than me)
Certainly, our solution is failing at this last litmus. We are in trouble again. Need Help!!!
I heard, one of our old friends, whom we never bother to call so far, can help here. So let’s knock her door. By the way, her name Decorator. Here we are at the door
Me: (Knock knock)
Someone from inside: Who is this?
Me: Hey I was looking for Decorator. Is this a right place where I can find her? I seriously need her help.
Door Opens.
Decorator: I am decorator. I am glad that you came here to help. Many programmers ignore me, either because they don’t know me or my power.
Me: What power you are talking about?
Decorator: Ability to add behavior without touching existing code.
Me: Sounds interesting. I am looking for something like that only. Could you elaborate on this.
Decorator: why not, here is what GoF says about me:
Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
Me: As always, that was big bumper for me ?
Decorator: Don’t worry. Let me simplify it for you. Remember me, when you have requirement to add extra behavior on top of existing one, without changing original code.
Me: I am with you. Go ahead
Decorator: So when it comes to add behavior, all you have to do is implement existing interface, in your case, ILogger, and keep reference to original one in your new class. So the new class looks like this
public class DecoratedMSMQLogger : ILogger
{
private readonly ILogger _logger;
private readonly IMSMQWritter _msmqWritter;
public DecoratedMSMQLogger(IMSMQWritter textFileWritter, ILogger logger)
{
_msmqWritter = textFileWritter;
_logger = logger;
}
public void Write(string message)
{
_msmqWritter.Write(message);
if (_logger != null)
_logger.Write(message);
}
}
Me: Ok, and now all I am doing is passing each calls to that logger, after I am done with mine
Decorator: Exactly. And by doing this, what you just followed is called, OCP.
Me: you solved my problem. Thank you so much and I promise to visit you again and again
Decorator: my pleasure, bye bye
Me: bye
PS: sample code already uploaded in my google repository.
Monday, June 9, 2008
Strategy Pattern in Action
Here is the requirement cheat that we received from our client.
- An Eagle is a Bird.
- A Parrot is a Bird.
- A Crow is a Bird.
- A Sparrow is a Bird.
- All Bird can fly.
- Eagle and Crow fly the same way i.e. they print “FAST FLY” when the method is invoked.
- Parrot and Sparrow fly the same way i.e. they print “SLOW FLY” when the method is invoked.
- All birds eat.
- Eagle and Parrot Eat the same way i.e. they print “EAT GRASS” when the method is invoked.
- Crow and sparrow eat the same way i.e. they print “EAT FRUITS” when the method is invoked.
Our goal is to implement this, with in most maintainable fashion or call it best possible solution, which we can think of.
Sounds simple, isn’t it?
All we need is one interface, call it IBird, with fly and eat method. And then Eagle, Parrot, Crow and Sparrow will implement this interface. Base on our current thinking, our class diagram looks something like this:
public interface IBird
{
string Fly();
string Eat();
}
public class Eagle : IBird
{
public string Fly()
{
return "FAST FLY";
}
public string Eat()
{
return "EAT GRASS";
}
}
public class Parrot : IBird
{
public string Fly()
{
return "SLOW FLY";
}
public string Eat()
{
return "EAT GRASS";
}
}
public class Crow : IBird
{
public string Fly()
{
return "FAST FLY";
}
public string Eat()
{
return "EAT FRUITS";
}
}
public class Sparrow : IBird
{
public string Fly()
{
return "SLOW FLY";
}
public string Eat()
{
return "EAT FRUITS";
}
}
Wait a minute!! Are we sure this is best possible thing we can do here? Is this most maintainable/flexible solution that we can possibly have??
Let’s think over this!!!
Aren’t we repeating our self when we implement Fast Fly method in Eagle as well as Crow? Same thing goes for slow fly, eat grass and eat fruit method too. Clearly, we have more than one instance of our algorithms. So it fails on maintainability test (because now we have to remember that every time we change something or fixes some issue, we have to do it at two places)
Let’s run flexibility test now. Our client has come up with following new requirement
A Plane is not a bird.
Plane fly the same way as Eagle and Crow i.e. it print “FAST FLY” when the method is invoked.
Hmmm….. Since we have same behavior as in bird, how about implementing IBird and thereby get the behavior.
public class Plane : IBird
{
public string Fly()
{
return "FLY FAST";
}
public string Eat()
{
throw new System.InvalidOperationException();
}
}
STOP!!!! Does this make sense? C’mon… after all, plane is not a bird. And what does it eat? May be fuel, but our client didn’t specified any requirement related to that. One option could be to leave it as not implemented though.
Grrrrrrrrr!!! You are now violating ISP. Sounds like we are failing on this test too... Shame on us, couldn’t clear even one test.
We need help. F1 please!
Somebody is knocking our door. Let me go and open the door.
Me: Hey, who is there?
Voice from outside: Hey it’s me, Strategy pattern. I heard you screaming for help. I think I can help you. May I come in?
Me: hmmm… sure, why not? So why do you think you can help me with solving my problem?
Strategy Pattern: Well, you see, you are trying to put everything in inheritance base model and that is why you are falling in trap. Rather you can use compositional approach.
Me: ha ha ha… that’s too much jargon, just like Microsoft help. Now can you come to the point and tell me how to solve my problem, instead?
Strategy Pattern: Sure, lets re-consider our approach to this problem here. From the description, it is clear that we are talking about two different set of behavior, Flying and Eating, right?
Me: Ya, I can see that as well.
Strategy Pattern: ok, so lets define two interface called ICanFly and ICanEat, to represent them.
Me: sounds good to me.
Strategy Pattern: Now since we have Fast flyer and Slow flyer, we can implement them in class called FastFlyer and SlowFlyer respectively.
Me: Go on
Strategy Pattern: And then, we have FruitEater and GrassEater , which implement ICanEat. That makes our class diagram look like this:
public interface ICanFly
{
string Fly();
}
public interface ICanEat
{
string Eat();
}
public class FastFlyer : ICanFly
{
public string Fly()
{
return "FAST FLY";
}
}
public class SlowFlyer : ICanFly
{
public string Fly()
{
return "SLOW FLY";
}
}
public class GrassEater : ICanEat
{
public string Eat()
{
return "EAT GRASS";
}
}
public class FruitEater : ICanEat
{
public string Eat()
{
return "EAT FRUITS";
}
}
Me: I am with you. Carry on
Strategy Pattern: Finally, Eagle is fast flyer and eats grass so why not let it implement ICanFly and ICanEat interfaces. In our implementation of Fly, we will delegate this task to FastFlyer and for Eat we will delegate to GrassEater.
Me: Now you are going to tell, we can do same thing for Parrot, Crow and Sparrow too, right?
Strategy Pattern: you are smart! So that gives us new picture like this:
public class Eagle : ICanFly, ICanEat
{
public string Fly()
{
return new FastFlyer().Fly();
}
public string Eat()
{
return new GrassEater().Eat();
}
}
public class Parrot : ICanFly, ICanEat
{
public string Fly()
{
return new SlowFlyer().Fly();
}
public string Eat()
{
return new GrassEater().Eat();
}
}
public class Crow : ICanFly, ICanEat
{
public string Fly()
{
return new FastFlyer().Fly();
}
public string Eat()
{
return new FruitEater().Eat();
}
}
public class Sparrow : ICanFly, ICanEat
{
public string Fly()
{
return new SlowFlyer().Fly();
}
public string Eat()
{
return new FruitEater().Eat();
}
}
Me: Wow, you see that solves my problem, because I am no more repeating my algorithm here. It’s been implemented at just one place
Strategy Pattern: Of course. Do you see any other benefit too?
Me: Hmmm. Let me think………………….
Me: Yes, you see, now I can have plane which implements only ICanFly interface and I am going to delegate actual implementation to FastFlyer again.
Strategy Pattern: right, that gives you
public class Plane : ICanFly
{
public string Fly()
{
return new FastFlyer().Fly();
}
}
Me: And it also passes my second test of flexibility because I could change my design to accommodate new enhancement, very easily.
Strategy Pattern: There you go.
Me: Thank you so much strategy pattern. I am glad that you came here and taught me such an important lesson.
Strategy Pattern: I am glad too, that I could be of some use.
Me: Last thing, I would like to ask though, how do I recognize you in future? Do you have any formal identity?
Strategy Pattern: Oh ya, I am defined as
“Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.”
Me: sounds great, and once again thanks for this valuable lesson. I guess we will meet again very soon J
Strategy Pattern: bye bye, and Develop smartly J
-------------------------------------------------X-------------------------------------------------
That was end of my first session on the ongoing series of blog about design pattern. I am planning to upload all my code sample on google code repository. So watch out at http://feeds.feedburner.com/MahendraMavani for more update.
Also, along with my colleague, John Teague, I am going to record screen cast on strategy pattern. This screen cast will briefly cover what I have discussed here and then we will jump to real life usage scenario of strategy pattern in action.
Disclaimer: I am not claiming to be design pattern expert. This is just 2 cent from my side to feel the ocean. I welcome comments, suggest, concern, query and healthy argument on this post. Please feel free to drop your opinion in the comment section.
Wednesday, June 4, 2008
Why do we need to know design pattern?
Probably, we all know definition of design pattern from wiki that its general reusable solution to a commonly occurring problem. And of course we have thousands of web pages over internet, telling about these patterns every now and then. Aha! Don’t forget those dotfactory for GoF.
So the natural question that comes in my mind is, WHY one more? Here is the answer I got from within
While many people know these patterns by definition and by class diagram, what is missing really is what they actually mean when it comes to applying them in practical scenario. Most importantly knowing when to apply and when not to (Nothing is as bad as applying design pattern at wrong place). Besides, what matter more in practical cases are design principles and not just blind application of any pattern (just because you know that pattern). After all ultimate goal behind any design pattern is to encapsulate what is changing.
With that in mind, I am planning to start with series of blog on design pattern, along with my friend John Teague. Our goal is to
- Uncover design principle buried behind each design pattern
- Dig bit more into practical application of pattern over just definition and class diagram
- Going beyond “General reusable solution”
- Finally, disclosing why everything you hear about these patterns from your friend is almost wrong.
- Avoiding those silly coupling mistake which gifts you sleepless night just before release.
Here are few important bookmarks, which you might be interested in keeping eye on:
http://www.lostechies.com/blogs/johnteague/
http://mahendramavani.blogspot.com/
Alternatively, you can subscribe to our RSS feed at
http://www.lostechies.com/blogs/johnteague/rss.aspx
http://feeds.feedburner.com/mahendramavani
I will soon post about overall outline of this series of blog. In short, as of now, idea is to cross post on both of above space and each design pattern will followed a screen cast which will demonstrate example discussed in the blog. These screen case will be a very good example of pair programming too.
Watch this space… much more interesting to come
Develop smartly :)
