The Decorator Pattern is a structural design pattern that allows you to dynamically add behavior to objects without modifying their existing code. In C#, the decorator pattern is commonly used when extending functionality in a flexible and reusable way while following the Open/Closed Principle.
In this guide, you'll learn:- What the Decorator Pattern is
- When to use it
- UML structure explanation
- Step-by-step C# implementation
- Real-world example
- Benefits and drawbacks
Decorator Pattern in C#: Complete Guide with Real-World Examples
Getting Started
Modern software development demands flexibility, scalability, and maintainability. As applications grow, adding new functionality to existing classes without breaking or modifying tested code becomes increasingly important. This is where the Decorator Pattern in C# plays a crucial role.
The Decorator Pattern is a structural design pattern that allows developers to dynamically add new behavior to objects at runtime without altering their structure. Instead of modifying an existing class or creating numerous subclasses, the decorator pattern uses object composition to extend functionality in a clean and maintainable way.
In simple terms:The Decorator Pattern wraps an existing object inside another object that adds new behavior.This approach follows key object-oriented design principles such as:
- Open/Closed Principle – Classes should be open for extension but closed for modification.
- Single Responsibility Principle – Functionality can be divided across focused classes.
- Composition over Inheritance – Favor object composition instead of deep inheritance hierarchies.
When to Use the Decorator Pattern in C#
Use the decorator pattern when:- You want to add functionality without modifying existing code.
- You need runtime flexibility in adding behavior.
- Subclassing would lead to too many subclasses.
- You want to follow the Single Responsibility Principle.
Decorator Pattern Structure (UML Concept)
The pattern consists of:- Component – Common interface
- ConcreteComponent – Base implementation
- Decorator – Abstract class implementing Component
- ConcreteDecorator – Adds new behavior
In C#, Decorator Design Pattern Example
We’ll build a coffee ordering system where:- Basic coffee has a cost.
- You can add Milk, Sugar, or Whipped Cream dynamically.
- Each addition increases the price and description.
Component Interface
public interface ICoffee
{
string GetDescription();
double GetCost();
}
Concrete Component
public class SimpleCoffee : ICoffee
{
public string GetDescription()
{
return "Simple Coffee";
}
public double GetCost()
{
return 5.0;
}
}
Base Decorator
public abstract class CoffeeDecorator : ICoffee
{
protected ICoffee _coffee;
public CoffeeDecorator(ICoffee coffee)
{
_coffee = coffee;
}
public virtual string GetDescription()
{
return _coffee.GetDescription();
}
public virtual double GetCost()
{
return _coffee.GetCost();
}
}
Concrete Decorators
Milk Decoratorpublic class MilkDecorator : CoffeeDecorator
{
public MilkDecorator(ICoffee coffee) : base(coffee) { }
public override string GetDescription()
{
return _coffee.GetDescription() + ", Milk";
}
public override double GetCost()
{
return _coffee.GetCost() + 1.5;
}
}
Sugar Decorator
public class SugarDecorator : CoffeeDecorator
{
public SugarDecorator(ICoffee coffee) : base(coffee) { }
public override string GetDescription()
{
return _coffee.GetDescription() + ", Sugar";
}
public override double GetCost()
{
return _coffee.GetCost() + 0.5;
}
}
Whipped Cream Decorator
public class WhippedCreamDecorator : CoffeeDecorator
{
public WhippedCreamDecorator(ICoffee coffee) : base(coffee) { }
public override string GetDescription()
{
return _coffee.GetDescription() + ", Whipped Cream";
}
public override double GetCost()
{
return _coffee.GetCost() + 2.0;
}
}
Usage
class Program
{
static void Main(string[] args)
{
ICoffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
coffee = new WhippedCreamDecorator(coffee);
Console.WriteLine("Order: " + coffee.GetDescription());
Console.WriteLine("Total Cost: $" + coffee.GetCost());
}
}
Output
Order: Simple Coffee, Milk, Sugar, Whipped Cream
Total Cost: $9.0
How It Works
Each decorator:- Implements the same interface (ICoffee)
- Wraps another ICoffee
- Adds behavior before/after delegating to the wrapped object
The Core Concept Behind the Decorator Pattern
The key idea behind the decorator pattern is simple but powerful:- Define a common interface.
- Create a concrete implementation of that interface.
- Create decorator classes that:
- Implement the same interface
- Hold a reference to an object of that interface
- Add extra behavior before or after delegating calls
Because decorators implement the same interface, they can replace the original object seamlessly. This is known as transparent wrapping.
So at runtime, the object structure looks like: WhippedCreamDecorator
→ SugarDecorator
→ MilkDecorator
→ SimpleCoffee
Summary
The Decorator Pattern is a powerful structural design pattern in C#. It enables developers to add responsibilities to objects without altering existing code. By wrapping objects inside decorators that implement the same interface, you gain runtime flexibility and cleaner architecture.
Thanks