Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / Java

Decorator Pattern (A Layman to Laymen)

Rate me:
Please Sign up or sign in to vote.
4.89/5 (73 votes)
25 Apr 2010CPOL12 min read 79.4K   514   113   17
This article takes a scenario based approach to make the reader understand the Decorator pattern

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.

C#
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.

  1. For certain messages, we need to validate the user before reading and writing the file.
  2. 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.
  3. 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.

EncryptedMessageInheritence.jpg

Similarly you derive SecureMessageWriter (class for user validation) from EncryptedMessageWriter.

SecureMessageInherited.jpg

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.

image004.gif

The four participants are:

  1. Component: Interface for the objects that can have responsibilities added to it dynamically. In our case IMessageWriter <code>and IMessageReader.
  2. 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.
  3. 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.
  4. 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.

SecureMessageWriter.jpg

EncryptedMessageWriter.jpg

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.

C#
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:

C#
IMessageWriter msgWriter = 
    new SecureMessageWriter(new EncryptionMessageWriter(new MessageWriter()));
  1. Decorators have saved us from making a complex base class, with plenty of code, which won’t be needed in most of the instances.
  2. They allow us to make different combinations, and sequences of different behaviors, which was not easily possible otherwise.
  3. 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.

C#
ArrayList al = new ArrayList();
al = ArrayList.Synchronized(al);
//Now al can be treated just as ArrayList
//but actually it's an instance of SyncArrayList
//which is derived from ArrayList 

And SyncArrayList will store the ArrayList passed in the property _list and will override different instance methods of the ArrayList this way.

C#
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.

C#
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.

  1. 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.
  2. 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
  3. 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Pakistan Pakistan
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionMY vote for 5 Pin
mohitatwork14-Oct-13 10:51
mohitatwork14-Oct-13 10:51 
GeneralMy vote of 5 Pin
Arpit Mandliya22-Mar-13 20:47
Arpit Mandliya22-Mar-13 20:47 
QuestionProxy? Pin
Giovani Guizzo22-Feb-13 3:04
Giovani Guizzo22-Feb-13 3:04 
Generaljava i/o Pin
Java Experience30-Jan-13 19:09
Java Experience30-Jan-13 19:09 
GeneralMy vote of 5 Pin
cFullerene12-Jun-12 4:33
cFullerene12-Jun-12 4:33 
GeneralMy vote of 5 Pin
HaBiX12-May-12 9:34
HaBiX12-May-12 9:34 
GeneralMy vote of 5 Pin
Guillaume Ranslant3-Dec-11 22:25
Guillaume Ranslant3-Dec-11 22:25 
GeneralMy vote of 5 Pin
Lakshmipathy12-Jun-11 23:59
Lakshmipathy12-Jun-11 23:59 
GeneralExcellent Saqib Pin
SameerBhatti7-Jun-11 21:07
SameerBhatti7-Jun-11 21:07 
GeneralExcellent! Pin
Sharjith15-Feb-11 7:43
professionalSharjith15-Feb-11 7:43 
GeneralRe: Excellent! Pin
Syed Saqib Ali Tipu27-Feb-11 21:01
Syed Saqib Ali Tipu27-Feb-11 21:01 
GeneralMy vote of 5 Pin
karim85926-Jan-11 3:47
karim85926-Jan-11 3:47 
GeneralTotally disagree PinPopular
PatLeCat21-Apr-10 4:10
PatLeCat21-Apr-10 4:10 
GeneralRe: Totally disagree Pin
Syed Saqib Ali Tipu21-Apr-10 6:30
Syed Saqib Ali Tipu21-Apr-10 6:30 
AnswerRe: Totally disagree Pin
Eric Xue (brokensnow)6-Sep-10 15:07
Eric Xue (brokensnow)6-Sep-10 15:07 
GeneralRe: Totally disagree Pin
vijsy4-Oct-10 3:24
vijsy4-Oct-10 3:24 
GeneralRe: Totally disagree Pin
wizardzz24-Nov-10 10:03
wizardzz24-Nov-10 10:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.