Decorator Pattern
The Decorator Pattern provides a flexible alternative to sub classing for extending functionality dynamically.
The idea of the Decorator Pattern is to wrap an existing class, add other functionality to it, then expose the same interface to the outside world. Because of this our decorator exactly looks like the original class to the people who are using it.
It is used to extend or alter the functionality at runtime. It does this by wrapping them in an object of the decorator class without modifying the original object. So it can be called a wrapper pattern.
Advantages of Decorator Pattern
- Adds functionality to existing objects dynamically
- Alternative to sub classing
- Flexible design
- Supports Open Closed Principle
When to use Decorator Pattern
- Legacy System
- Contorls
- Sealed Classes
The following class diagram shows us the Decorator Pattern's design:
- Component: It defines the interface of the actual object that needs functionality to be added dynamically to the ConcreteComponents.
- ConcreteComponent: The actual object in which the functionalities could be added dynamically.
- Decorator: This defines the interface for all the dynamic functionalities that can be added to the ConcreteComponent.
- ConcreteDecorator: All the functionalities that can be added to the ConcreteComponent. Each needed functionality will be one ConcreteDecorator class.
A decorated class is a base class of concrete decorator classes and it inherits from the base component class. So the decorators can be used in the place of concrete components.
Let's start learning the Decorator Pattern with one real time problem. Suppose we need to build an application for a car show room that sells cars along with car accessories. Economy, Deluxe and Luxury are the car categories that come with the optional accessories packages Basic, Advanced and Sport. So the application must provide the car cost adding the cost of accessory package if the user chooses any of the preceding packages.
Okay. Let's start designing our application with the ICar interface that will be implemented by Economy, Deluxe and Luxury Car category classes.
-
-
-
- public interface ICar
- {
- string GetDescription();
- double GetCost();
- }
-
-
-
-
- public class EconomyCar : ICar
- {
- public string GetDescription()
- {
- return "Economy Car";
- }
-
- public double GetCost()
- {
- return 450000.0;
- }
- }
-
-
-
-
- public class DeluxCar : ICar
- {
- public string GetDescription()
- {
- return "Delux Car";
- }
-
- public double GetCost()
- {
- return 750000.0;
- }
- }
-
-
-
-
- public class LuxuryCar : ICar
- {
- public string GetDescription()
- {
- return "Luxury Car";
- }
-
- public double GetCost()
- {
- return 1000000.0;
- }
- }
We are good up to this. But what about the optional accessory packages? How we will design them? Can we design them using subclassing like the following?
- public class EconomyCarWithBasicAccessories: ICar{
- ..
- ..
- }
- public class DeluxCarWithBasicAccessories: ICar{
- ..
- ..
- }
- public class LuxuryCarWithBasicAccessories: ICar{
- ..
- ..
- }
-
- ..
- ..
- ..
- ..
- public class EconomyCarWithSportAccessories: ICar{
- ..
- ..
- }
- public class DeluxCarWitSportAccessories: ICar{
- ..
- ..
- }
- public class LuxuryCarWithSportAccessories: ICar{
- ..
- ..
- }
Yes we can build our classes like the above. But the class count keeps on increasing by 4 whenever we introduce either one car category or one accessory package into our system. Because of this application size will become huge and after some time it will be difficult to manage.
To avoid this we can get help from our hero the Decorator Pattern. As I already said, this pattern is used to attach or add extra functionality to the object at run time without altering the class structure. That means there is no need to create extra sub classes whenever we introduce a new car category or new accessory packages.
Let's build our system using the Decorator Pattern.
The following is the class diagram of the above design.
Here we successfully avoided the sub classing issue. Whenever we need to add a new car category we only must add one class for it like the following.
- public class SuperLuxury : ICar
- {
- public string GetDescription()
- {
- return "Super Luxury Car";
- }
-
- public double GetCost()
- {
- return 1500000;
- }
- }
And we need to add only one class for a new accessory package for our system.
- pubic classs ExtremeSportAccessories : CarAccessoryDecorator
- {
- public ExtremeSportAccessories(ICar aCar)
- :base(aCar)
- {
- }
- public override string GetDescription()
- {
- return base.GetDescription() +",Extreme Sport Accessories";
- }
- public override double GetCost()
- {
- return base.GetCost() + 25000;
- }
- }
Nice design, right? But how to extend the functionality of an instance at run time? Good question. The following code will explain that.
- static void Main(string[] args)
- {
-
- ICar objCar = new EconomyCar();
-
-
- CarAccessoriesDecorator objAccessoriesDecorator = new BasicAccessories(objCar);
-
-
- objAccessoriesDecorator = new AdvancedAccessories(objAccessoriesDecorator);
-
- Console.Write("Car Detials: " + objAccessoriesDecorator.GetDescription());
- Console.WriteLine("\n\n");
- Console.Write("Total Price: " + objAccessoriesDecorator.GetCost());
-
- Console.Read();
- }
In the above we created an instance of EcomomyCar and we added accessories functionality, one by one, by wrapping the instance with the accessories instances.
I hope you enjoyed this article. Thank you.