The Decorator Design Pattern is a structural pattern in software development that allows behavior to be added to individual objects, either statically or dynamically, without affecting the behavior of other objects from the same class.
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.
Components of Decorator Design Pattern
- 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.
Example in C#
In C#, let's illustrate the Decorator Pattern with an example involving a beverage system, where we have a base Beverage class representing different types of drinks.
- Component(IBeverage.cs):
1234567891011
namespace
DecoratorPattern.Component
{
/// <summary>
/// Interface for beverage
/// </summary>
public
interface
IBeverage
{
string
GetDescription();
double
Cost();
}
}
- Concrete Component(Coffee.cs):
123456789101112131415161718
namespace
DecoratorPattern.Component
{
/// <summary>
/// Concrete component representing a basic beverag
/// </summary>
public
class
Coffee : IBeverage
{
public
string
GetDescription()
{
return
"Coffee"
;
}
public
double
Cost()
{
return
5.0;
}
}
}
- Decorator(BeverageBaseDecorator.cs):
123456789101112131415161718192021222324252627
using
DecoratorPattern.Component;
namespace
DecoratorPattern.Decorator
{
/// <summary>
/// Decorator base class
/// </summary>
public
abstract
class
BeverageBaseDecorator : IBeverage
{
protected
IBeverage _beverage;
public
BeverageBaseDecorator(IBeverage beverage)
{
_beverage = beverage;
}
public
virtual
string
GetDescription()
{
return
_beverage.GetDescription();
}
public
virtual
double
Cost()
{
return
_beverage.Cost();
}
}
}
- ConcreteDecorator(MilkDecorator.cs and SugarDecorator.cs):
123456789101112131415161718192021222324
using
DecoratorPattern.Component;
namespace
DecoratorPattern.Decorator
{
/// <summary>
/// Concrete decorator adding milk to the beverage
/// </summary>
public
class
MilkDecorator : BeverageBaseDecorator
{
public
MilkDecorator(IBeverage beverage) :
base
(beverage)
{
}
public
override
string
GetDescription()
{
return
$
"{_beverage.GetDescription()}, Milk"
;
}
public
override
double
Cost()
{
return
_beverage.Cost() + 0.5;
// Milk costs 0.5
}
}
}
123456789101112131415161718192021222324using
DecoratorPattern.Component;
namespace
DecoratorPattern.Decorator
{
/// <summary>
/// Concrete decorator adding sugar to the beverage
/// </summary>
public
class
SugarDecorator : BeverageBaseDecorator
{
public
SugarDecorator(IBeverage beverage) :
base
(beverage)
{
}
public
override
string
GetDescription()
{
return
$
"{_beverage.GetDescription()}, Sugar"
;
}
public
override
double
Cost()
{
return
_beverage.Cost() + 0.2;
// Sugar costs 0.2
}
}
}
- Client:
12345678910111213141516
// Create a base coffee
using
DecoratorPattern.Component;
using
DecoratorPattern.Decorator;
IBeverage coffee =
new
Coffee();
// Decorate the coffee with milk
coffee =
new
MilkDecorator(coffee);
// Decorate the coffee further with sugar
coffee =
new
SugarDecorator(coffee);
// Output the description and cost of the final beverage
Console.WriteLine($
"Description: {coffee.GetDescription()}"
);
Console.WriteLine($
"Cost: ${coffee.Cost()}"
);
Console.ReadLine();
This example demonstrates how you can dynamically add responsibilities to objects at runtime using the Decorator Pattern in C#. Each decorator transparently adds its own behavior to the component it decorates without affecting the behavior of other objects from the same class.
Output
Advantages of Decorator Pattern
- Adds functionality to existing objects dynamically
- Alternative to sub classing
- Flexible design
- Supports Open Closed Principle
The full source code is available here:
Happy coding!! 😊
No comments:
Post a Comment