Dynamically extending responsibilities using Decorator Pattern

Definition

“Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub classing for extending functionality”

Closed for Modification and Open for Extension

One of the main challenges we face in development is Change. The Closed for Modification and Open for Extension principle says a new functionality can be added by keeping the original code unchanged.

Example:

You have an Album class today which is just blank now. Tomorrow if the customer wants a Christmas tree on that.. What would be our approach?

We will modify the original Album class to incorporate the Christmas tree on it. This should not be the best approach. There is a better approach to this. We can still keep the Album class unchanged and add the Christmas tree to it. Everything happens in the runtime… that is the cool part of it.

Some more examples

We can see real life controls like Form, Button etc. There would be a Form class which having inbuilt functionality. Still the user can use it and add new controls on it / extend the functionality. Here we will be basically deriving from the existing Form/Button class and add new methods or properties to it.

The difference between the above approach and Decorator pattern is that, in the Decorator pattern, we are assigning the responsibility in the runtime.

Conclusion on Change

So basically we can conclude that whenever changes are required, the possible solutions could be:

· Change the original class

· Subclass it and create instance of subclass

· Use Decorator Pattern and still using the original class instance

Here we are going to see how we can use Decorator Pattern to help with the following scenario.

Requirement

The requirement here would be to provide a default Album object and based on dynamic requirement from the user in runtime, we have to draw other pictures to the album.

clip_image002

Design

Our first class would be the Album class which has a Graphics object as parameter.

It contains a Draw() method which is virtual and just clears the graphics object.

public class Album

{

public Graphics Graphics

{

get;

set;

}

public Album()

{

}

public Album(Graphics graphics)

{

Graphics = graphics;

}

public virtual void Draw()

{

Graphics.Clear(Color.White);

}

}

Decorator

We are adding the class named AlbumDecorator which will serve as the base class for all decorators.

public abstract class AlbumDecorator : Album

{

protected Album _album;

public AlbumDecorator(Album album)

{

_album = album;

}

public override void Draw()

{

_album.Draw();

}

}

It takes an Album class as parameter in the constructor.

There are ChristmasTreeDecorator, SantaClausDecorator, StarDecorator deriving from AlbumDecorator:

public class ChristmasTreeDecorator : AlbumDecorator

public class SantaClausDecorator : AlbumDecorator

public class StarDecorator : AlbumDecorator

Each class deriving from AlbumDecorator would be having it’s own picture to draw.

Invoking

In the main form we create an instance of Album class and assigns it to form field _album.

private Album _album;

_album = new Album(_graphics);

_album.Draw();

In the runtime, when user wanted Christmas Tree, instance of ChristmasTreeDecorator is created.

_album = new ChristmasTreeDecorator(_album);

_album.Draw();

In the above code we can see the same _album.Draw() method is called.

How it works

Whenever we call the Draw() method of a decorator class, it in turns calls the original Album.Draw(). After that it will call it’s own Draw() method. In this way we can pass the same album instance to multiple decorators. If there are 10 decorators, all the decorator Draw() methods will be invoked.

You can test this placing a breakpoint inside the StarDecorator Draw() method.

Note

Using decorator we can add dynamic responsibilities to an object in runtime. This provides us the flexibility of creating instance of decorators on need base. This would provide real advantage in scenarios like the additional responsibility instance consists of much memory usage.

Leave a Reply

Your email address will not be published. Required fields are marked *