Edit notes: No Contents edited, just fixed the link.
Introduction
A friend of mine always used to say that the toughest part of learning any programming discipline is to run the “Hello World”. After that, everything is easy. Years later, I realized how right he was. This is the basic goal behind this series on design patterns, to help out people getting started with these. It especially targets those who have strong OOP concept but are struggling with design patterns, and can’t figure out how to apply them in their designs. I don’t claim to write some comprehensive reference on different design patterns, but I do hope that this series will help you getting started with these. Designs patterns can’t be tied to a specific language. Even though I provide most of my code examples in C#, I will try avoiding C# specific constructs as much as possible, and hence targeting maximum readers, especially the ones working in languages influenced by C++ object model.
Decorator pattern allows us to add additional behaviors to the objects dynamically. Just like in my previous article, Strategy Pattern Primer, we will first go through a scenario. We will also look at the alternate approaches, which will help us realize the real power of this design pattern, and the flexibility it offers.
The Thought Process
The scenario we will follow today may not be very realistic and sometimes pretty weird, but it will help us understand the concepts and power underlying this pattern. The goal is to add the functionality of storing messages coming from different sources on the hard disk in the form of text files. So first of all we defined the intention (interface) and then implemented it. IMessageWriter
, and IMessageReader
interfaces for writing and reading messages respectively, as well as their implementations are given below.
interface IMessageWriter
{
string Message { set; }
void WriteMessage(string filePath);
}
class MessageWriter : IMessageWriter
{
private string message;
#region IMessageWriter Members
public string Message
{
set { this.message = value; }
}
public virtual void WriteMessage(string filePath)
{
File.WriteAllText(filePath, this.message);
}
#endregion
}
interface IMessageReader
{
string ReadMessage(string filePath);
}
class MessageReader : IMessageReader
{
#region IMessageReader Members
public virtual string ReadMessage(string filePath)
{
if (File.Exists(filePath))
return File.ReadAllText(filePath);
else return null;
}
#endregion
}
The message will be stored in the Message
property while WriteMessage
of MessageWriter
is just writing it on the file path passed to it. Similarly MessageReader
’s function ReadMessage()
will read it back from the file and return it as a text string
. Let’s say you receive new requirements from the customer.
- For certain messages, we need to validate the user before reading and writing the file.
- For some messages, we want them to be saved encrypted so no one would be able to read it from the file. But we need to save this encrypted message in base4 encoded string.
- For some messages, we need both of these functionalities.
Pretty weird haa ;). First we will analyze different solutions without using Decorators. This will make us realise how powerful this simple design pattern is.
Classical Solutions
You decide to use inheritence as it will allow you to build upon the base behavior. You choose to implement the encryption behavior in EncryptedMessageWriter
, derived from MessageWriter
.
Similarly you derive SecureMessageWriter
(class for user validation) from EncryptedMessageWriter
.
Now we can Write encrypted messages as well as encrypted messages with user validation. What about the case where we need to write simple text message without encryption but with user validation. You can use some dirty trick of placing some decision making code in EncryptedMessageWriter
which will make it skip encryption when not needed. Let’s say you still choose this option. How about a different sequence of operations, i.e., we want to encrypt first, then we will validate, and if not validated then we will do something else with the based64 encoded encrypted message. Obviously this case cannot be handled with the above hierarchy. And who can stop the customer from requesting more features like some messages should be digitally signed, larger messages should be compressed with or without encryption as required, for some messages, after writing them on disk, you must enter the file path and timestamp in MessageQueue
for some other application to read, or even in the database. And so on and on and on.
To assess the gravity and complexity of the situation you’re in, let’s focus on just validation, forgetting about the details of hierarchy. Currently, we have the implementation of validation in case of encrypted message. Now we need to have the same functionality in many other cases, e.g., for CompressedMessageWriter
, DigitallySignedMessageWriter
, etc. The only choice you have is to implement SecureCompressedMessageWriter
, SecureDigitallySignedMessageWriter
etc. Similarly for plenty of other combinations like encrypted message compression, simple message compression, and so on. Damn, you are really in Sub Class Hell.
The second solution is to write a pretty hefty MessageReader
and keep on adding all the functionality as the requirement arrives. Increasing its complexity and making it hard to maintain, over time. The most unrecommended approach.
A third solution might be a combination of the above two, which may alleviate the problem but won’t take it away.
Decorator Pattern Enters The Scene
This is exactly the kind of problem decorator pattern solves. If you look carefully at the above solution involving inheritence, you will realise that the root of the trouble is the static
relationship introduced by inheritence. This relationship is embedded into the classes which cannot be changed at runtime. Decorators replace this relationship with containment, an object relationship which is much more flexible and can be updated at runtime.
First, let’s see what decorator pattern actually is. Following is the class diagram for decorator pattern.
The four participants are:
- Component: Interface for the objects that can have responsibilities added to it dynamically. In our case
IMessageWriter <code>
and IMessageReader
. - ConcreteComponent: Defines an object implementing Component interface. This is the object that is going to be decorated, but it doesn’t have any knowledge of decoration. The person implementing the decorator may not even have the access to the source code. In our case
MessageWriter
and MessageReader
. - Decorator: Maintains a reference to the
Component
object, and defines an interface that conforms to the Component
interface. So it contains a reference to the base behavior, and also implements the same interface, hence can be treated as the Component
itself. The client code expecting the Component
will deal with Decorator without even noticing the difference. - ConcreteDecorator: This actually adds responsibility to the component. The class that inherits from Decorator and may add some additional specific functionality in the form of new
public
methods.
Up to now we have two things: Component
, the base behavior, in our case IMessageWriter
and for reading IMessageReader
, and ConcreteComponent
, our implementations of writing and reading behaviors, MessageWriter
and MessageReader
respectively.
Now here are our implementations of SecureMessageWriter
and EncryptedMessageWriter
.
Where the Hell is your Decorator?????
I just said there are four participants in this pattern, I have shown you Component (IMessageReader
, IMessageWriter
), ConcreteComponent (MessageReader
, MessageWriter
) and ConcreteDecorators (SecureMessageWriter
, EncryptedMessageWriter
). But where is the Decorator? In our case, we are just adding to already existing behavior and introducing no new behavior. We are not changing the hierarchy either. In that case, we can ignore implementing Decorator and follow the main hierarchy. I am not showing the Reader classes as these are just the reverse process.
What Have We Achieved
Now, when I need a simple message writing with user validation I will do.
IMessageWriter msgWriter = new SecureMessageWriter(new MessageWriter());
I am decorating the MessageWriter
with SecureMessageWriter
and it will now validate user before writing the message on the disk. In case of needing Encrypted message writing along with Validation, I will do:
IMessageWriter msgWriter =
new SecureMessageWriter(new EncryptionMessageWriter(new MessageWriter()));
- Decorators have saved us from making a complex base class, with plenty of code, which won’t be needed in most of the instances.
- They allow us to make different combinations, and sequences of different behaviors, which was not easily possible otherwise.
- Rather than implementing different subclasses for each different behavior and combination, we have implemented each required behavior separately which we can add as needed.
Back to the Real World
In this section, we will see some real world examples of the usage of Decorator pattern.
Synchronization Wrappers
People who used good old collection classes in .NET, like Queue
, ArrayList
, etc., may still remember the function synchronized(collection), exposed by many of these classes. It takes the collection instance itself as a parameter and returns a synchronized collection. Collection classes themselves are not synchronized, but what this method actually returns is a decorator derived from the collection class itself. For example, in the code below, when we call Syncrhonized(al)
, we will receive an instance of SyncArrayList
, a class derived from ArrayList
.
ArrayList al = new ArrayList();
al = ArrayList.Synchronized(al);
And SyncArrayList
will store the ArrayList
passed in the property _list
and will override different instance methods of the ArrayList
this way.
public override int Add(object value)
{
lock (this._root)
{
return this._list.Add(value);
}
}
A Word Of Caution
While creating your own synchronization wrappers this way, be careful of the phenomenon called self-deadlock, which means a thread already having a lock will enter another method (or in the same method in case of recursion) where it will try to obtain the lock on the same object again. In Windows whether you use .NET’s implementation of Monitors, or kernel level named or unnamed mutexes, all are re-entrant i.e. recursive. So you will not face this problem but when programming in other environments like Linux where default mutex type is Fast mutex, a non-recursive mutex, your code may become a victim of self-deadlock. In case of using a semaphore even on Windows, it also has no sense of ownership, and gives you this problem if you are not careful. Of course for a binary semaphore i.e. with n=1, only on second attempt you will be in a self deadlock.
On the same model, you may implement a read-only wrapper of you collection classes. And unlike what we have seen till now, they rather than adding functionality to the classes, will take away some. For example in overridden Add()
method one may throw OperationNotSupportedException
. .NET offers ReadonlyCollection<T>
which is a wrapper around generic list. Java provides unmodifiable wrappers, e.g., unmodifiableCollection
, unmodifiableSet
, etc.
In Java, you also get synchronization wrappers for many collection types, e.g.
List<t> list =
Collections.synchronizedList(new ArrayList<t>());
IO in Java and .NET
Java’s java.io
package and .NET’s Stream
classes employ this pattern. I will not go into the details of their underlying implementations. In .NET, Stream
, which is an abstract
class, is providing the base behavior (our Component), whereas classes like FileStream
, MemoryStream
are ConcreteCompents
and classes like BufferedStream
, CryptoStream
etc. are Concrete decorators, which like Filestream
, and MemoryStream
are derived from abstract Stream
class. You can clearly notice that their implementation is missing the Decorator as well.
Similarly in Java BufferedReader
and FilterReader
are decorators of Reader
, whereas BufferedReader
is further decorated by LineNumberReader
, and FilterReader
by PushbackReader
.
What's the Catch
This pattern allows us to provide the extensibility point in your implementation. As you might have noticed, while implementing my decorators I never even touched my component classes. So even if one doesn’t own a class, one can decorate it to add new behaviors dynamically and even recursively. This additional behavior might be added before or after the base behavior, or this behavior might be blocking it. The decorators can provide new methods and even new properties as you can see in the class diagram. But there are some problems associated with decorators.
- The programmer who is going to use decorators must understand their intention; otherwise he may end up using some senseless combination or sequence. For example in our scenario if the programmer defines the sequence in this way: message will be compressed, then encrypted, and then validated, it will be a bit senseless. In real life scenarios, the results might be disastrous for some combinations or ,sequences.
- Testing a decorator class will require providing a mock of decorated class. Since we haven’t yet covered testing, so I will not discuss it
- Also this pattern puts some responsibility on the base behavior designer. Although it's perfectly legitimate to add new properties or
public
methods in the decorator, but this way it will lose the flexibility of being handled by the base reference, and you will need to use the concrete instance
Now Some Clearances
I thank all who provided me the feedback on my previous article on strategy pattern. That has guided me writing this one. Many have accused me of oversimplification in one way or other. Well I assert that I haven’t oversimplified anything. These words might be a bit misleading. What I am trying to say is that their feedback was correct, as simplification was my goal. But oversimplification is a bit harsh. Those people aren’t totally wrong, as I have avoided mixing up design patterns with programming idioms. What I am presenting in this series is the underlying principles and a guideline on where to apply the pattern. The underlying principles are simple; it’s the problem that gives you hard time. Recognizing parts of the problem that are candidate for design patterns might be tricky and requiring experience. Sometimes you won’t even realize that a certain piece of code is a good candidate for applying some design pattern, till many cycles of refactoring have been performed. This gives us another reason for analyzing the problem and recognizing the different design patterns to save us from plenty of future refactoring as they are already the result of heavy refactoring. However that feedback helped me a lot, and in future I will try covering languages specific implementations too (provided I’ll have enough time). The next article will be on Creation patterns covering Simple Factory, Factory Method, Abstract Factory and Builder. Once again your feedback is more than welcome, identifying the areas where i couldn't clear things up, or the points of improvement, thanks.
About the Source Code
When I started writing this article I planned of posting code for java and .Net. But due to tight schedules lately, I have only managed to provide the code for .Net. It's a Visual Studio 2008 solution file. I forgot putting readme file for the solution, which I am attaching now.