Intuition

Imagine a basic coffee:

  • Start with plain coffee.

  • Optionally add milk.

  • Optionally add sugar.

  • Optionally add whipped cream.

You don’t want separate classes for every combination like MilkSugarCoffee, WhippedMilkCoffee, etc.
Instead, you take a base coffee object and wrap it in small objects, each adding one feature.
That is the intuition behind the Decorator Design Pattern: wrap an object to extend its behavior without touching the original class.


Intent of the Decorator pattern

The core intent:

Attach additional responsibilities to an object dynamically, using wrappers instead of subclasses.

Key ideas:

  • There is a component interface that both the core object and decorators implement.

  • A concrete component provides the basic behavior.

  • A decorator holds a reference to a component and delegates to it, adding behavior before or after the delegation.

  • Multiple decorators can be stacked, building complex behavior from simple layers.

You get an extension by composition, not by deep inheritance trees.


Basic structure

Typical roles:

  • Component

    • Interface or abstract base class for all objects that can be decorated.

  • ConcreteComponent

    • Basic implementation of the component.

  • Base Decorator

    • Implements Component and stores a reference/pointer to another Component.

    • Forwards calls to the wrapped component.

  • Concrete Decorators

    • Derive from the base decorator.

    • Override methods to add extra work around the delegated call.

This is often called a “wrapper” because the decorator wraps the original object.


C++ example: Coffee with add‑ons

Step 1: Component interface


Step 2: Concrete component


Step 3: Base decorator


The base decorator implements Beverage and holds a pointer to another Beverage.
Concrete decorators will override the methods and delegate to beverage.

Step 4: Concrete decorators (Milk, Sugar)


Each decorator:

  • Calls the wrapped object’s method (beverage->description(), beverage->cost()).

  • Adds its own behavior (extra text and extra cost).

Step 5: Using decorators (stacking)


Flow:

  • Start with PlainCoffee.

  • Wrap it with MilkDecorator.

  • Wrap the result with SugarDecorator.

  • The final object is still aBeverage*, but behavior = “plain coffee + milk + sugar”.


Why not just use inheritance?

Using only inheritance, you might end up with:

  • PlainCoffee

  • MilkCoffee

  • SugarCoffee

  • MilkSugarCoffee

  • WhippedMilkSugarCoffee

  • and so on.

Problems:

  • Class explosion: every new feature multiplies combinations.

  • Features are hard‑coded; changing combinations requires new subclasses.

  • Behavior is fixed at compile time.

The decorator improves this by:

  • Moving each extra feature into its own decorator class.

  • Allowing features to be composed at runtime by stacking decorators.

  • Keeping the base component simple and stable (good for the Open‑Closed Principle).


Real‑world analogies and uses

Common places where Decorator appears:

  • I/O streams: a base stream wrapped with buffering, filtering, compression, etc.

  • GUI components: adding scrollbars, borders, and shadows to a basic window by wrapping it.

  • Cross‑cutting concerns: logging, caching, authentication added around service calls by wrapping the original service.

These all follow the same pattern: keep the core simple, and wrap it when you want extra behavior.


When to use Decorator

A decorator is a good choice when:

  • You want to add responsibilities to objects dynamically, not just via fixed subclasses.

  • You want to avoid a huge subclass hierarchy of all feature combinations.

  • Different contexts need different combinations of features on the same base type.

It may be overkill when:

  • You don’t need runtime flexibility; one or two simple subclasses would do.

  • Too many nested decorators would make debugging and reasoning about behavior difficult.

A useful test: whenever you think “start with X and optionally add A, B, C features in various combinations,” Decorator is worth considering.