CodeBlanks Logo Icon CodeBlanks

Decorator

C# course · Advanced · Lesson 65/83

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

  1. 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.

  2. Declare the inner field

    private ____ ICoffee _inner;
    Show answer
    private readonly ICoffee _inner;
  3. Match Decorator parts

    Component
    Shared interface
    ConcreteComponent
    Wrapped original object
    Decorator
    Wrapping base class
    Wrapped
    Inner component

All lessons in Advanced