| Type | Structural |
| Purpose | Add responsibilities to individual objects dynamically without affecting other objects of the same class. |
Description
The Decorator pattern allows responsibilities to be added to an individual object dynamically at runtime.
Unlike inheritance, which changes the behaviour of an entire class, decoration applies the change to a specific object instance without altering the underlying class itself.
A decorator works by wrapping another object and presenting the same interface, while adding new behaviour before, after, or around the delegated operation. In this way, decorators can be layered to combine responsibilities flexibly, without requiring a large inheritance hierarchy.
This is especially useful when the same kinds of additional behaviour may need to be applied across many different objects. Rather than embedding every variation directly into concrete classes, those responsibilities can be separated into distinct decorators and composed as needed.
Decorator also supports the Single Responsibility Principle, since each concrete component and each decorator can remain focused on its own specific behaviour.
Decorator is primarily concerned with adding responsibilities to objects dynamically.
Overview
| Component | This is the interface that both the original (or "concrete") objects and the decorators should adhere to. |
| ConcreteComponent | A concrete implementation of the Component interface. It's the object we will dynamically add new behavior to. |
| Decorator | An abstract class or interface that conforms to the Component interface and maintains a reference to another Component, which may be a ConcreteComponent or another Decorator. |
| ConcreteDecorators | These are the actual objects that add new responsibilities or behavior to the ConcreteComponents. There can be many different ConcreteDecorators, all extending Decorator, each providing different behavior. |
First Implementation
// Component
public interface IPizza
{
decimal Cost();
}
// ConcreteComponent
public class PlainPizza : IPizza
{
public decimal Cost() => 10.0m;
}
// Decorator
public abstract class PizzaDecorator(IPizza pizza) : IPizza
{
protected IPizza Pizza = pizza;
public abstract decimal Cost();
}
// ConcreteDecorators
public class Tomato(IPizza pizza) : PizzaDecorator(pizza)
{
public override decimal Cost() => Pizza.Cost() + 2.5m;
}
public class Cheese(IPizza pizza) : PizzaDecorator(pizza)
{
public override decimal Cost() => Pizza.Cost() + 1.5m;
}
// Client
public static class DecoratorDemo
{
public static void Execute()
{
IPizza pizza = new PlainPizza();
pizza = new Tomato(pizza);
pizza = new Cheese(pizza);
Console.WriteLine($"Cost of pizza: {pizza.Cost()}");
}
}
// outputs:
// Cost of pizza: 14.0
Second Implementation
An Abstract Decorator class isn't always necessary. In this example, each ConcreteDecorator directly implements the Component interface and maintains a reference to another Component. The decorators can still be nested to build more complex behaviour.
// Component
public interface IPizza
{
string Make();
}
// ConcreteComponent
public class Pizza : IPizza
{
public string Make() => "Pizza with";
}
// ConcreteDecorators
public class CheeseDecorator(IPizza pizza) : IPizza
{
public string Make() => pizza.Make() + " Cheese";
}
public class TomatoDecorator(IPizza pizza) : IPizza
{
public string Make() => pizza.Make() + " Tomato";
}
// Client
public static class DecoratorDemo
{
public static void Execute()
{
var pizza = new Pizza();
var cheesePizza = new CheeseDecorator(pizza);
var cheeseTomatoPizza = new TomatoDecorator(cheesePizza);
Console.WriteLine(cheeseTomatoPizza.Make());
}
}
// outputs:
// Pizza with Cheese Tomato
Third Implementation
Here's an example of decorating an entity with behavior, then removing it later:
// Component
public interface IEntity
{
string Name { get; }
void Move();
void Act();
}
// ConcreteComponent
public class Entity(string name) : IEntity
{
public string Name => name;
public void Move()
{
Console.WriteLine($"{Name} is moving normally.");
}
public void Act()
{
Console.WriteLine($"{Name} is acting normally.");
}
}
// Concrete Decorator
public class DrunkenEntity(IEntity entity) : IEntity
{
public string Name => entity.Name;
public void Move()
{
Console.WriteLine($"{entity.Name} is stumbling around the place.");
}
public void Act()
{
Console.WriteLine($"{entity.Name} wants to beat up Uglúk and eat hobbit legs.");
}
public IEntity RemoveEffect() => entity;
}
// Client
public static class DecoratorDemo
{
public static void Execute()
{
IEntity orc = new Entity("Grishnákh");
orc.Move();
orc.Act();
orc = new DrunkenEntity(orc);
orc.Move();
orc.Act();
if (orc is DrunkenEntity drunk)
{
orc = drunk.RemoveEffect();
}
orc.Move();
orc.Act();
}
}
// outputs:
// Grishnákh is moving normally.
// Grishnákh is acting normally.
// Grishnákh is stumbling around the place.
// Grishnákh wants to beat up Uglúk and eat hobbit legs.
// Grishnákh is moving normally.
// Grishnákh is acting normally.
Summary
For more information, please see here.