This time we are going to solve logging problem.
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.
Showing posts with label OCP. Show all posts
Showing posts with label OCP. Show all posts
Saturday, July 12, 2008
Subscribe to:
Posts (Atom)