Decorator
Learn behavior extension with the Decorator pattern.
Decorator pattern
Decorator wraps an object inside another class that implements the same interface, extending behavior without inheritance. A field holds the inner object; the decorator forwards calls inward and adds its own behavior.
Example: ICoffee declares Cost() and Description(). SimpleCoffee is the concrete component. MilkDecorator takes an ICoffee, adds 0.50 to the cost and "Süt" to the description. Nesting decorators produces combos like "Sade + Süt + Şeker".
Exercise Wrap coffee with MilkDecorator
Define interface ICoffee with decimal Cost() and string Description(). SimpleCoffee returns 10m and "Sade". MilkDecorator takes an ICoffee, adds 0.50m to Cost(), and appends " + Süt" to Description(). In Main, build new MilkDecorator(new SimpleCoffee()) and print "{description}: {cost}".
Show example solution
ICoffee { public decimal Cost() => 10m; public string Description() => "Sade"; }. Write MilkDecorator : ICoffee { private readonly ICoffee _inner; public MilkDecorator(ICoffee inner) => _inner = inner; public decimal Cost() => _inner.Cost() + 0.50m; public string Description() => _inner.Description() + " + Süt"; }. In Main: var c = new MilkDecorator(new SimpleCoffee()); Console.WriteLine($"{c.Description()}: {c.Cost()}");
Example solution:
using System;
interface ICoffee
{
decimal Cost();
string Description();
}
class SimpleCoffee : ICoffee
{
public decimal Cost() => 10m;
public string Description() => "Sade";
}
class MilkDecorator : ICoffee
{
private readonly ICoffee _inner;
public MilkDecorator(ICoffee inner) => _inner = inner;
public decimal Cost() => _inner.Cost() + 0.50m;
public string Description() => _inner.Description() + " + Süt";
}
class Program
{
static void Main()
{
var coffee = new MilkDecorator(new SimpleCoffee());
Console.WriteLine($"{coffee.Description()}: {coffee.Cost()}");
}
}
Decorator chains
The power of Decorator is nesting. A chain like new SugarDecorator(new MilkDecorator(new SimpleCoffee())) adds both milk and sugar. Each layer takes the inner result, adds its own contribution, and passes it outward.
With inheritance, each combination needs its own subclass (CoffeeWithMilk, CoffeeWithSugar, CoffeeWithMilkAndSugar...). Decorator avoids this class explosion.
Test yourself
-
When is the Decorator pattern used?
- To close a class to inheritance
- To add static members
- To extend behavior without inheritance
- To limit the number of instances
Show answer
To extend behavior without inheritance — Correct. Decorator adds behavior by wrapping; it does not multiply subclasses.
-
Declare the inner field
private ____ ICoffee _inner;Show answer
private readonly ICoffee _inner; -
Match Decorator parts
- Component
- Shared interface
- ConcreteComponent
- Wrapped original object
- Decorator
- Wrapping base class
- Wrapped
- Inner component