Decorator in C++

Structural Medium

In one sentence: Decorator attaches extra responsibilities to an object dynamically, by wrapping it in another object with the same interface — a flexible alternative to subclassing, and the wrappers stack.

Real-world analogy

You dress for a cold morning. You start in a shirt. It's freezing, so a sweater goes over the shirt. The wind picks up, so a coat goes over the sweater. It starts to snow, so a scarf goes over that.

Every garment went on the same way — over whatever was already there — and each one added exactly one thing: warmth, windproofing, a covered neck. Nothing underneath was altered to make room. The shirt did not become a warmer shirt; it is still a plain shirt, with a sweater over it.

And the layers come off as easily as they went on. Step into a warm shop, take the coat off, put it back on at the door. You never had to own a single fused coat-sweater-shirt garment for that particular weather, and you'd have needed a wardrobe full of them to cover every combination of cold, wind and snow.

Mapping the analogy to the pattern:

  • The shirt you started in → the ConcreteComponent, the plain thing at the centre of the stack that does the base job.
  • Each garment pulled on over it → a ConcreteDecorator, adding one responsibility and delegating the rest to what's underneath.
  • "Can be worn, and can be worn over" → the shared Component interface; every layer satisfies it, which is exactly why a layer can sit on top of another layer.
  • The order you put them on → decorator nesting. Scarf over coat and coat over scarf are both legal and not the same outfit.
  • Taking the coat off in the shop → withdrawing a responsibility at run time — and wearing two sweaters is the same decorator applied twice, which subclassing can't do cleanly.

Another angle: a coffee order. Espresso, then milk, then hazelnut syrup, then whipped cream. Each addition still hands you a drink you can hold and sip — same interface — and the price accumulates one layer at a time.

The problem

A notification library starts by sending email. Then users want SMS. Then Slack. Then some users want all three at once.

  1. Subclassing explodes. SmsNotifier, SlackNotifier, SmsAndSlackNotifier, EmailAndSmsAndSlackNotifier — every combination needs its own class.
  2. The choice is made at runtime. Which channels a user wants comes from a settings row, not from the source code, so compile-time inheritance can't express it.
  3. Editing the base class doesn't scale. Cramming every channel into one Notifier with a pile of boolean flags makes one class that everyone has to modify forever.

GoF's version is a UI toolkit where any widget might need a border, or scroll bars, or both. Inheriting a border means every instance of that subclass has one, decided statically at compile time — the client never gets to say "this particular text view, and only this one, gets a border."

The solution

Make each extra behavior its own wrapper object that implements the same interface as the thing it wraps:

  1. Define the interface (Notifier::send).
  2. Write one plain implementation — the concrete component (email).
  3. Write a base decorator that holds a Notifier and simply forwards calls to it.
  4. Each concrete decorator inherits from the base decorator, forwards the call, and does its extra bit before or after.

Because a decorator is a Notifier and holds a Notifier, you can nest them arbitrarily. Three channels needs three classes, not eight. The transparency is what makes the nesting legal: since a decorator is indistinguishable from the thing it wraps, it can go anywhere the component can — including inside another decorator.

Note: Two pieces of GoF implementation advice save real pain. First, keep the Component class lightweight — it should define an interface, not store data. A heavyweight component makes every decorator heavyweight too, which defeats the point when you're stacking them. Second, you can skip the abstract Decorator class when there's only one responsibility to add, which is common when you're decorating an existing hierarchy rather than designing a new one; just fold the forwarding into the single concrete decorator.

Structure

Structure of the Decorator pattern Structure of the Decorator pattern
Structure of the Decorator pattern

The shape that makes stacking work: Decorator both inherits from and holds a Component.

  • Component — the interface for objects that can have responsibilities added to them. In the example: Notifier.
  • ConcreteComponent — a plain object to which responsibilities can be attached. In the example: EmailNotifier.
  • Decorator — keeps a reference to a Component and conforms to Component's interface, forwarding every operation to the object it holds. In the example: NotifierDecorator.
  • ConcreteDecorator — adds the responsibility: it calls through to the wrapped component and does its own work before or after. In the example: SmsNotifier and SlackNotifier.

C++ example

An alerting system where channels are stacked at runtime.

#include <iostream>
#include <memory>
#include <string>
#include <utility>

class Notifier {
public:
    virtual ~Notifier() = default;
    virtual void send(const std::string& message) = 0;
};

// The concrete component everything else wraps.
class EmailNotifier : public Notifier {
public:
    void send(const std::string& message) override {
        std::cout << "Email: " << message << "\n";
    }
};

// Base decorator: is a Notifier, holds a Notifier, forwards to it.
class NotifierDecorator : public Notifier {
public:
    explicit NotifierDecorator(std::unique_ptr<Notifier> wrapped)
        : wrapped_(std::move(wrapped)) {}
    void send(const std::string& message) override { wrapped_->send(message); }
private:
    std::unique_ptr<Notifier> wrapped_;
};

class SmsNotifier : public NotifierDecorator {
public:
    using NotifierDecorator::NotifierDecorator;
    void send(const std::string& message) override {
        NotifierDecorator::send(message);   // let the inner layers run first
        std::cout << "SMS: " << message << "\n";
    }
};

class SlackNotifier : public NotifierDecorator {
public:
    using NotifierDecorator::NotifierDecorator;
    void send(const std::string& message) override {
        NotifierDecorator::send(message);
        std::cout << "Slack: " << message << "\n";
    }
};

int main() {
    std::unique_ptr<Notifier> plain = std::make_unique<EmailNotifier>();
    plain->send("Disk 80% full");

    std::cout << "--- escalating ---\n";

    std::unique_ptr<Notifier> loud =
        std::make_unique<SlackNotifier>(
            std::make_unique<SmsNotifier>(
                std::make_unique<EmailNotifier>()));
    loud->send("Server down");
}
Output
Email: Disk 80% full
--- escalating ---
Email: Server down
SMS: Server down
Slack: Server down

Both plain and loud are just a Notifier* to the caller — one message went out on one channel, the other on three, with no new class per combination.

When should you use it?

GoF's applicability list, in plain speech. Use Decorator:

  • To add responsibilities to individual objects dynamically and transparently — that is, without other objects of the same class being affected, and without clients noticing.
  • For responsibilities that can be withdrawn again, not just added.
  • When extending by subclassing is impractical: either a large number of independent extensions would explode into one subclass per combination, or the class definition is hidden or otherwise unavailable to subclass.

Tip: Order matters. Encrypt(Compress(stream)) and Compress(Encrypt(stream)) produce very different bytes — encrypted data barely compresses. If your decorators aren't commutative, document the intended stacking order.

Pros and cons

Pros (GoF's consequences)

  • More flexible than static inheritance. Responsibilities are attached and detached at run time, and you can mix and match them freely — you can even apply the same one twice (two borders, two retry layers), which inheriting from a class twice can't do cleanly.
  • It avoids feature-laden classes high in the hierarchy. Decorator is pay-as-you-go: start with a simple class and add functionality incrementally, so an application never pays for features it doesn't use.
  • New decorators are independent of the classes they extend, so you can write them for extensions nobody foresaw.

Cons (also GoF's)

  • A decorator and its component are not identical. The wrapper is a transparent enclosure, but it is a different object — so never rely on object identity (pointer comparison, address-keyed maps) once decorators are in play.
  • Lots of little objects. Decorator-heavy designs end up as swarms of small objects that all look alike and differ only in how they're wired together. Easy to customize if you built it; hard to learn and hard to debug if you didn't.
  • Removing a specific layer from the middle of a stack is awkward.
  • Behavior depends on wrapping order, which is easy to get wrong and invisible in the type.

How to remember it

Memory hook: "Same coat check, more layers." You hand back an object of exactly the type you were given — it just does more. The three wrappers separate cleanly by what they do to the interface: Decorator keeps the interface and adds behavior, Adapter changes the interface, Proxy keeps the interface and controls access. And against its other neighbour: a Decorator is a Composite with exactly one child, but it exists to add, not to aggregate.

  • Adapter — a decorator changes only an object's responsibilities, never its interface; an adapter gives an object a completely new interface.
  • Composite — GoF calls a decorator a degenerate composite with a single component, but the intent differs: decorators add responsibilities, they don't aggregate.
  • Strategy — a decorator changes an object's skin; a strategy changes its guts. Prefer Strategy when the component class is intrinsically heavyweight, since the strategy only needs its own small interface rather than the component's whole one.
  • Proxy — same interface and same wrapping shape, but it controls access rather than adding features, and usually manages the wrapped object's lifetime itself.
  • Chain of Responsibility — also a chain of handlers, but any link may stop the request; every decorator normally passes it along.