Design Pattern articles in this series are listed below:
Design Patterns - An Overview
Design Patterns – Using the Strategy Pattern in C#
Design Patterns – Using the Singleton Pattern in C#
Design Patterns – Using the Adapter Pattern in C#
Design Patterns - Using the Facade Pattern in C#
Design Patterns - Using the Composite Pattern in C#
Design Patterns - Using the Decorator Pattern in C#
Design Patterns - Using the Abstract Factory Pattern in C#
Design Patterns – Using the Builder Pattern in C#
Design Patterns - Using the Observer Pattern in C# (Events and Delegates)
Design Patterns – Using the Chain of Responsibility Pattern in C#
Design Patterns – Using the State Pattern in C#
Design Patterns – Which to use When? Take the Quiz!
What is a Decorator Pattern?
Decorator Patterns belong to the Structural Pattern category and it’s role is in providing a way of attaching new behavior to the object at run time. The object is unaware of the new behavior and this pattern is a good candidate for enhancing legacy applications. Decorator Pattern provides a way of adding functionality to an existing class without using inheritance.
A key characteristic of Structural Patterns is that they can be used not only during the creation of a system but also during maintenance and enhancements. Since the Decorator pattern allows us to extend the behavior of an instance of a class rather than the class itself, the underlying class remains untouched.
How is the Decorator Pattern implemented?
The essential players in the Decorator Pattern are:
1. Component – This is the original class implementation
2. IComponent – The interface that identifies the class of objects that can be decorated
3. Decorator Base Class – The base class for decorator concrete classes
4. Decorator Concrete Class(es) – One or more classes that provide the implementation for the Decorators.
5. Client – A consumer class
Let us implement the Decorator pattern with an example. Let us assume we are maintaining an application for a car manufacturing company. When the application was created, the company manufactured only one type of car – the Boring car. The implementation of the Boring car class is as shown below.
public interface ICar
{
string Description { get; }
int Price { get; }
}
public class BoringCar : ICar
{
public string Description
{
get
{
return "Boring Car";
}
}
public int Price
{
get
{
return 20000;
}
}
}
The BoringCar class (Component) is a simple class that implements the ICar (IComponent) interface and implements the Description and the Price properties. Over several years, the customers demanded that they be provided with different options to customize the car. The management decided to provide two different options. One to customize the paint based on the customer needs and the other an option to fit the car with a turbo charged engine.
|
Add Custom Paint and Turbo Charged Engine to the Boring car to make an Exciting Car
(Pics source: www.sxc.hu) |
|
|
Boring Car
|
|
Amazing Car
|
To enhance the application to support customizations, we could extend the BoringCar’s functionality by creating derived classes such as CarWithCustomPaint or CarWithTurboChargedEngine. Although the possibilities are mathematically finite, it would be difficult in maintaining the application. In future, if the management decides to provide more options, it would be practically impossible to create classes that can be used for different combinations.
By using the Decorator pattern, we could combine different options at run time. First, we need to create a Decorator base class.
//Car Decorator Base Class
public abstract class CarDecorator : ICar
{
private ICar car { get; set; }
public CarDecorator(ICar car)
{
this.car = car;
}
public virtual string Description
{
get { return car.Description; }
}
public virtual int Price
{
get { return car.Price; }
}
}
The CarDecorator class implements the ICar interface. Note that this class includes both the “is-a” and a “has-a” relationship. It not only uses Composition (by declaring a private variable of ICar type) but also uses Inheritance (by implementing the ICar interface). Also, it makes the Description and the Price properties virtual so that the Decorator classes can override them.
The concrete Decorator classes inherit from the CarDecorator class. In our example, we need 2 decorators – one for the Custom Paint and the other one for the Turbo Charged Engine.
//Decorator 1 - Custom Paint
public class CustomPaintDecorator : CarDecorator
{
public CustomPaintDecorator(ICar car) : base(car) { }
public override string Description
{
get
{
return base.Description + " with Custom Paint";
}
}
public override int Price
{
get
{
return base.Price + 5000;
}
}
}
//Decorator 2 - Turbocharged engine
public class TurboChargedEngineDecorator : CarDecorator
{
public TurboChargedEngineDecorator(ICar car) : base(car) { }
public override string Description
{
get
{
return base.Description + " with Turbo Charged Engine";
}
}
public override int Price
{
get
{
return base.Price + 10000;
}
}
}
The overridden Price property adds the additional cost to the base cost. The client creates 3 different versions of the car – A Boring Car, An Exciting Car and an Amazing Car.
namespace DecoratorPattern
{
class Program
{
static void Main(string[] args)
{
//Manufacturing a Boring Car
ICar boringCar = new BoringCar();
Console.WriteLine(String.Format("{0} costs {1}", boringCar.Description, boringCar.Price.ToString("c")));
//Manufacturing an Exciting Car = Boring Car + Custom Paint
ICar excitingCar = new CustomPaintDecorator(
new BoringCar()
);
Console.WriteLine(String.Format("{0} costs {1}", excitingCar.Description, excitingCar.Price.ToString("c")));
//Manufacturing an Amazing Car = Boring Car + Custom Paint + Turbocharged engine VRRROOM..
ICar amazingCar = new TurboChargedEngineDecorator(
new CustomPaintDecorator(
new BoringCar()
)
);
Console.WriteLine(String.Format("{0} costs {1}", amazingCar.Description, amazingCar.Price.ToString("c")));
}
}
}
When the application is run, the output shows how much the price of each car is.
Physical Model – Class Diagram
Conclusion
The Decorator Pattern is simple yet powerful pattern that is particularly useful for extending the functionality of existing systems. The code is simple to read, easy to implement and enhance. Do not forget to take a look at other Design Pattern articles on this site. You can also follow DotNetCube on Twitter or subscribe to the RSS feed.