New day, a new pattern. Today we will start to discuss structural patternsdeeply starting with the Decorator pattern.
Really, I am trying to collect as many resources as possible for this huge topic to be valuable and to add something wasn’t published before so I am in parallel reading the famous book Gang of four, also C# 3.0 Design Patterns by O’Reilly, and some other tutorials and stuff.
I liked the way that O’Reilly book takes to discuss each pattern so I will reuse the headlines for each pattern but I will try to add more value to the content. So, let’s start discussing Decorator pattern.
1 – Role
Decorator pattern’s role is to provide a way to add new state and behavior dynamically. The decorated object doesn’t know that it is being decorated which is very useful for evolving systems.
2 – Illustration
As its name suggests, it is used to add or remove (decorate) objects at runtime with new state or new behavior. The best example that I have ever read to illustrate this pattern is the photo management system which lets you edit some aspects of the photo like adding new border, tag, or caption.
Consider we have a photo and we want to add a border to it, may be a border and caption, or may be caption only. If you just think a bit, you will find that a combination of photo and border makes another object which is photo with border. So every combination may result in a different (unknown) object at runtime which means we can have endless ways to customize the photo.
Also, I liked the illustration of the Gang of four book which is:
Suppose we have a TextView object that displays text in a window. TextView has no scroll bars by default, because we might not always need them. When we do, we can use a ScrollDecorator to add them. Suppose we also want to add a thick black border around the TextView. We can use a BorderDecorator to add this as well. We simply compose the decorators with the TextView to produce the desired result.
The beauty of this pattern is that:
- The original object is unaware of any decorations.
- There is no one big feature-laden class with all the options in it.
- The decorations are independent of each other.
- The decorations can be composed together in a mix-and-match fashion.
3 – Design
Reading here and there about this pattern, I have concluded that there are some minor variations in its design because of the language features you are implementing the pattern with. But the main guidelines remains.
I will start with a very general design (mentioned in the Gang of four book) then I will go through other variations step by step:
Basic Design
Players:
- ConcreteComponent
- An original class of objects that can have operations added or modified (there may be more than one such class).
- Component
- The base class that identifies the classes of objects that can be decorated (ConcreteComponent is one of these).
- Decorator
- The base class that identifies the common operations or state in the decorator classes and it inherits from the Component base class.
- ConcreteDecorator
- A class that inherits from the Decorator base class and adds state and/or behavior (there may be more than one such class).
- Operation
- An operation in Component objects that can be replaced (there may be several operations).
Looking into the previous players, we will see that we have two base classes Component and Decorator. Most of the variations in this pattern’s design results from changing or replacing those two classes as we will see now.
Use this basic design if:
- You are not the creator of this object structure, like that you have already made structure and you want to decorate it.
- You are the one who creates this structure but your using C++ or any language doesn’t support interfaces.
- You have default implementation in both Decorator and Component base classes.
- You have multiple decorators have common behavior and/or state defined in Decorator base class.
Variation 1
As you notice from the previous diagram, you can see that we have replaced the Component base class with an interface IComponent. Here we have to mention that in old days when Gang of four book was written, authors had implemented the patterns using C++ which doesn’t contain interfaces. In modern languages today like C# or Java contain the concept of interfaces.
I recommend always to use interfaces not abstract classes as long as you don’t have default implementation you want to add for all the subclasses.
Use this variation if you are using a language contains the interfaces concept and:
- You don’t have any default implementation in the Component base class and you have default implementation for the all concrete decorators.
- You are the one who is creating this object structure from scratch and have full control of it.
- You have multiple decorators have common behavior and/or state defined in Decorator base class.
Variation 2
Use this variation if you are using a language contains the interfaces concept and:
- You don’t have any default implementation in both Component base class and Decorator base class.
- You are the one who is creating this object structure from scratch and have full control of it.
- You have multiple decorators have common behavior and/or state defined in the IDecorator interface.
C# 3.0 Design Patterns by O’Reilly notified that IDecorator in C# has no purpose. And I didn’t get this note until now, but I see that if you have common behavior and/or state that you want to be in all decorators then you have to use either interface or a base class and depending in your situation you can select what is more appropriate from the other variations.
Variation 3
Use this variation if you are using a language contains the interfaces concept and:
- You don’t have any default implementation in both Component base class and Decorator base class.
- You are the one who is creating this object structure from scratch and have full control of it.
- You don’t have common behavior and/or state between decorators.
Common Design Guidelines
As we saw in the last four variations, you will see some common guidelines that I want to concentrate on them:
- All components must have a base class or interface.
- All decorators must be components. In other words, all decorators must inherit Component base class or implement IComponent interface.
- All decorators must wrap a component (Decorator must have a member of type Component or IComponent).
4 – Implementation
I will try here to give you a simple implementation of the decorator pattern. I will discuss how to implement the decorator pattern in general without considering any challenging examples or complicated scenarios.
I will use variation 3 to give you a simple implementation using the following UML class diagram:
Using C# or another candidate language, First we have to create IComponent Interface which will contain only one operation.
1 2 3 4 | public interface IComponent
{
void Operation();
}
|
Then we will create a Component class which will implement IComponent interface.
1 2 3 4 5 6 7 | public class Component : IComponent
{
public void Operation()
{
Console.WriteLine( "I love reading books." );
}
}
|
Now we want to decorate the Component object with a decorator. So let’s create one decorator class which will wrap an IComponent object and Implement the same interface IComponent interface.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class DecoratorA : IComponent
{
private IComponent _component;
public Decorator(IComponent component)
{
_component = component;
}
public void Operation()
{
_component.Operation();
Console.WriteLine( "And I love playing football." );
}
}
|
Another decorator class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class DecoratorB : IComponent
{
private IComponent _component;
public Decorator(IComponent component)
{
_component = component;
}
public void Operation()
{
_component.Operation();
Console.WriteLine( "And I love fishing." );
}
}
|
Now let’s build the client which will be a simple console application which will consume our Component and will decorate it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | using System;
namespace DecoratorPatternImplementation
{
class Program
{
static void Main( string [] args)
{
Console.WriteLine( "=== Original Component ===" );
IComponent component = new Component();
component.Operation();
Console.WriteLine();
Console.WriteLine( "=== Original Component Decorated by DecoratorA ===" );
IComponent decoratorA = new DecoratorA(component);
decoratorA.Operation();
Console.WriteLine();
Console.WriteLine( "=== Original Component Decorated by DecoratorB ===" );
IComponent decoratorB = new DecoratorB(component);
decoratorB.Operation();
Console.WriteLine();
Console.WriteLine( "=== Decorated Component By DecoratorA Decorated by DecoratorB ===" );
IComponent hybridDecorator = new DecoratorB(decoratorA);
hybridDecorator.Operation();
Console.WriteLine();
}
}
}
|
Run the console application and here is the output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | === Original Component ===
I love reading books.
=== Original Component Decorated by DecoratorA ===
I love reading books.
And I love playing football.
=== Original Component Decorated by DecoratorB ===
I love reading books.
And I love fishing.
=== Decorated Component By DecoratorA Decorated by DecoratorB ===
I love reading books.
And I love playing football.
And I love fishing.
Press any key to continue . . .
|
We have to point out to some important notices here:
- You can decorate a decorated component as we have seen in last example.
- Decorator pattern gives you the ability to mix and match between decorators.
- Decorators do not need any advanced language features; they rely on object aggregation and interface implementation.
5 – Finally
We are now ready to go deeply into this pattern by investigating some real world examples. Keep up, wait for the next article.