Saturday, 2 June 2012

Decorator Pattern

This is the first blog in a series that will focus on design patterns. I’ll be looking at some of the most popular design patterns in use today and  how they can  be used to improve the way you write code. I'll cover the key benefits of these patterns and use code examples to show you how they can be implemented. 

The decorator pattern attempts to address an important design principle, and that is to keep your code closed for modification but open for extension. The decorator pattern allows you to implement new logic with little or no change required to existing logic. This achieved by ensuring that new task specific logic is encapsulated by individual objects rather than being spread across your code. This encapsulation of task specific logic makes code much easier to maintain going forward. The decorator pattern allows you to use wrapper objects to extend existing code with new behaviour being delivered by augmenting new logic to existing code at runtime.
The following example describes a scenario where code is being repeatedly modified to satisfy new requirements. This is a scenario faced be developers every day and one which the decorator pattern aims to improve. The code below shows a very simple message processing utility. For the sake of this example we’ll imagine this message processor is consuming messages from a queue and processing them.
To start with our MessageHandler is required to append a message id to the incoming message. Note: I won’t be providing any implementation details of the specific tasks being performed (appending id’s, persisting to the database etc) as these code samples are here to describe common problems and how they can be solved with design patterns.
public class MessageHandler
{      
    public void processMessage()
    {
        // some code to append an id to message
    }
}
What if requirements change and suddenly we need the MessageHandler to write the incoming message to a log after it’s appended an id? One approach would be to update the existing MessageHandler class as shown below.
public class MessageHandler
{ 
     public void processMessage()
     {
        // some code to append an id to message
        // some more code to write message to log
     }
}
Now our message handler appends an id to the incoming message and writes it to a log. This works fine but we have our logic for both tasks bundled up in the same class. While this may be fine for trivial code (like our example) it can become a maintenance headache when dealing with a more verbose or complex code.
Imagine that suddenly the requirements change again and we need the MessageHandler to write the incoming message to a database. Once again we’ll have to modify our code to satisfy this new requirement as shown below.
public class MessageHandler
{     
     public void processMessage()
     {
        // some code to append an id to message
        // some more code to write message to logs
        // some more code to persist message in database
     }
}
This approach means that core code is modified every time a new requirement is implemented. This doesn't fit very well with the design principle we mentioned earlier,  keeping code closed for modification but open for extension.
The next example is going to show how the decorator pattern can be used to deliver the same functionality shown above but with more care taken to ensure that existing code can be extended without modification.
We’ll l start off with our standard MessageHandler object containing our core logic (appending an id to an incoming message).
public class MessageHandler
{
 public MessageHandler()
 {

 }

 /* core functionality implemented here */
 public void processMessage(String message_p)
 {
  // some code to append a message Id to message
  System.out.println("Appending Id 1234 to " + message_p + "...");
 }
}
Now we’ll create some decorator classes which will act as wrappers for the MessageHandler defined above. This means that variables holding a MessageHandler object should also be able to hold objects that wrap MessageHandler objects. An easy way to do this is to extend our wrapper (decorator) classes from the MessageHandler class.
/* abstract class extends core MessageHandler */
public abstract class MessageHandlerDecorator extends MessageHandler
{
 public abstract processMessage();
}
Because this class is abstract we cannot instantiate objects from it. Its purpose is to ensure that all wrappers (decorators) are consistent by forcing anyone who extends it to implement their own version of the processMessage method.
Next we’ll add new functionality to write the incoming message to a log after the id has been appended.  We’ll do this by creating a concrete wrapper class encapsulating our new logic. This wrapper object will invoke the processMessage method of the object that it is wrapping before running its own processMessage implementation. It’s this invocation of the wrapped object that allows us to dynamically augment new logic to an existing object at runtime.
/* LogMessageHanlder wrapper class contains our new logic - note
 * that it takes a MessageHandler as a constructor parameter */
public class LogMessageHandler extends MessageHandlerDecorator
{
 MessageHandler messageHandler_i;

 public LogMessageHandler(MessageHandler messageHandler_p)
 {
  messageHandler_i = messageHandler_p;
 }

 public void processMessage(String message_p)
 {
  // call handler passed in
  messageHandler_i.processMessage(message_p);

  // some code to write message to log
  System.out.println("Writing  " + message_p + " to log...");
 }
}
We’ll further extend the message handler functionality to persist incoming messages to the database.  We’ll do this by creating another concrete wrapper class. Again this wrapper will invoke the processMessage method of the object that it’s wrapping before running its own processMessage implementation. 
/* PersitenceMessageHandler wrapper class contains our new logic - note
 * that it takes a MessageHandler as a constructor parameter */
public class PersitenceMessageHandler extends MessageHandlerDecorator
{
 MessageHandler messageHandler_i;

 public PersitenceMessageHandler(MessageHandler messageHandler_p)
 {
  messageHandler_i = messageHandler_p;
 }

 public void processMessage(String message_p)
 {
  // call handler passed in
  messageHandler_i.processMessage(message_p);
  
  // some code to append a write message to log
  System.out.println("Persisting " + message_p + " in database...");
 }
}
We now have the following
  •  MessageHandler class - contains implementation of core functionality that we want to extend.
  • MessageHandlerDecorator - Ensures that all wrappers are consistent by forcing anyone who extends this class to implement their own version of the processMessage method.
  • LogMessageHandler - Wrapper class containing new functionality for logging message
  • PersistenceMessageHandler - Wrapper class containing new functionality for persisting message to the database
Next we’ll test our code and see the decorator pattern in action. 
public class TestMessageHandlers
{
 public static void main (String[] args_p)
 {
  String testMessage = "<TestMesssage>data</TestMessage>";

  /* configuration one */
MessageHandler messageHandler = new PersitenceMessageHandler(new LogMessageHandler(new MessageHandler()));
  messageHandler.processMessage(testMessage);
 }
}
When we invoke the processMessage method on MessageHandler it triggers the following sequence of events.
  1. PersistenceMessageHandler invokes the processMessage method of LogMessageHandler (which was passed in as an argument constructor)
  2. LogMessageHandler invokes the processMessage method of MessageHandlder (which was passed in as an argument constructor)
  3. The MessageHandler processMessage method runs and prints ‘Appending Id 1234 to <TestMesssage>data</TestMessage>...
  4. When the MessageHandler processMessage method returns LogMessageHandler runs and prints ‘Writing  <TestMesssage>data</TestMessage> to log...
  5. When the LogMessageHandler processMessage method returns PersistenceMessageHandler processMessage runs and prints ‘Persisting <TestMesssage>data</TestMessage> in database...
The diagram below shows how the message handlers are wrapped.

The output from this test program is as follows.

Appending Id 1234 to <TestMesssage>data</TestMessage>...
Writing  <TestMesssage>data</TestMessage> to log...
Persisting <TestMesssage>data</TestMessage> in database...

We were able to extend the original functionality provided by MessageHandler by decorating it with additional logic at runtime. This new logic was implemented without having to amend the original MessageHandler class. This is an example of how the decorator pattern allows us to keep our code closed for modification but open for extension.

The main benefits of the decorator pattern are
  • Greater separation of concerns as code is split into individual objects that address specific requirements.
  • Existing logic does not have to be modified. New functionality is delivered in separate objects which are augmented to existing logic at runtime. This reduces the likelihood of introducing regression issues into existing code.
  • Greater flexibility in the way in which we deliver functionality. For example, the objects can be decorated or wrapped in different sequences to deliver a different sequence of events. Our test class above could be easily changed as follows.
MessageHandler messageHandler2 = new LogMessageHandler(new PersitenceMessageHandler(new MessageHandler()));
messageHandler2.processMessage(testMessage);

This will result in the following output (note the ordering of the last 2 statements has changed).

                Appending Id 1234 to <TestMesssage>data</TestMessage>...
Persisting <TestMesssage>data</TestMessage> in database...
Writing  <TestMesssage>data</TestMessage> to log...

While the sample code in this blog is very simple indeed, it should be sufficient to demonstrate the mechanics of the decorator pattern and the benefits that it delivers.

2 comments:

  1. Good work Brian. Thanks for the example.

    Sam

    ReplyDelete
  2. very nice one :) I wasn't able to understand the idea "Decorator attaches the responsibilities run time while the inheritance attaches the responsibility at compile time" . This example made me understand :) whats that mean :) Good Job , Thanks Mate, Arun (Sydney)

    ReplyDelete