Early previewThis guide is in development. Items tagged verify are still being checked against official docs.
nebx Learn
GitHub
Menu
C++

C++

Zero-cost abstractions and full control over memory.

Paradigm
Multi-paradigm, OOP, generic
Typing
Static, strong-ish
Memory
Manual / RAII, no GC
Runs
Native machine code (AOT)
Concurrency
Threads, atomics, coroutines (C++20)
Structural pattern

Decorator

Intent

Attaches new behaviours to objects by placing them inside wrapper objects that share the same interface.

Problem

Users want notifications by email, SMS and Slack in any combination. Subclassing every combination explodes.

Solution

Wrap the base notifier in decorators. Each adds its behaviour and delegates to the wrapped object.

Real-world analogy

Think of it like this

Dressing in layers: a sweater when it's cold, a jacket over it, a raincoat on top when it rains. Each layer adds something and can come off.

Structure

association / extends implements / depends on
Client 5 «interface» Component + execute() 1 ConcreteComponent + execute() 2 BaseDecorator - wrappee: Component+ execute() 3 ConcreteDecorator + execute()+ extra() 4

Hover or tap a numbered role to highlight it in the diagram.

  1. 1Component — Component declares the interface shared by wrappers and wrapped objects.
  2. 2ConcreteComponent — Concrete Component is the object being wrapped; it defines the basic behaviour.
  3. 3BaseDecorator — Base Decorator holds a reference to a wrapped Component and delegates all work to it.
  4. 4ConcreteDecorator — Concrete Decorators add behaviour before or after calling the parent method.
  5. 5Client — Client wraps components in as many layers as it needs.

Try it

InteractiveWrap the notifier in layers, then send. Calls travel in, output comes back out.
Wrap with (in click order)

    
// output appears here

Code example

decorator.cpp
struct Notifier {
    virtual ~Notifier() = default;
    virtual void send(const std::string& m) = 0;
};
struct EmailNotifier : Notifier {
    void send(const std::string& m) override { std::cout << "email: " << m << '\n'; }
};

class NotifierDecorator : public Notifier {
public:
    explicit NotifierDecorator(std::unique_ptr<Notifier> w) : wrapped(std::move(w)) {}
    void send(const std::string& m) override { wrapped->send(m); }
protected:
    std::unique_ptr<Notifier> wrapped;
};

struct SlackDecorator : NotifierDecorator {
    using NotifierDecorator::NotifierDecorator;
    void send(const std::string& m) override {
        NotifierDecorator::send(m);
        std::cout << "slack: " << m << '\n';
    }
};

// auto n = std::make_unique<SlackDecorator>(std::make_unique<EmailNotifier>());

Applicability

Use Decorator when adding behaviour at runtime without changing the object's code.

Use Decorator when inheritance is awkward or impossible (final classes).

How to implement

  1. Make sure the domain can be a primary component with optional layers
  2. Define a component interface
  3. Create a concrete component
  4. Create a base decorator holding a wrapped component and delegating to it
  5. Create concrete decorators that add behaviour before or after delegating
  6. Let the client compose decorators

Pros and cons

Pros

  • Extend behaviour without subclassing
  • Combine behaviours by stacking wrappers
  • Single Responsibility per wrapper

Cons

  • Hard to remove a specific wrapper from the stack
  • Order of wrappers matters
  • Many small objects can be hard to debug

Check yourself

Check yourself

Add compression and then encryption around a file stream, in any combination, at runtime.

Saved in this browser only. Part of Structural patterns (0/7), Hands-on: patterns with live demos (0/5).