C++
Decorator
Learn behavior extension with the Decorator pattern.
What is the Decorator pattern?
Decorator wraps an object to extend its behavior without inheritance. Each decorator holds the wrapped object and adds its own behavior. This way many combinations are possible without a class explosion.
In C++ you define a Component abstract class. A Decorator class also derives from Component and holds a Component pointer. Concrete decorators (for example MilkDecorator) call the wrapped object's method and add their own contribution. Example: base coffee + milk + sugar, prices add up.
Concrete decorator and price sum
A concrete decorator derives from Decorator. In its cost() method it first calls inner->cost() then adds its own contribution. This builds a wrapping chain: each decorator takes the previous price and adds to it.
Example: BasicCoffee::cost() = 10, MilkDecorator adds 3, SugarDecorator adds 1. SugarDecorator(MilkDecorator(BasicCoffee)) gives 10 + 3 + 1 = 14. To add a new layer you only write a new decorator class; existing classes stay unchanged. This follows the single responsibility principle.
Exercise Write coffee decorators
Define an abstract Coffee with virtual int cost() const = 0;. BasicCoffee returns 10. A Decorator class derives from Coffee and holds a unique_ptr<Coffee> inner. MilkDecorator returns inner->cost() + 3. In main build a MilkDecorator(BasicCoffee) chain and print cost().
Show example solution
#include <iostream>
#include <memory>
class Coffee {
public:
virtual ~Coffee() = default;
virtual int cost() const = 0;
};
class BasicCoffee : public Coffee {
public:
int cost() const override { return 10; }
};
class Decorator : public Coffee {
protected:
std::unique_ptr<Coffee> inner;
public:
explicit Decorator(std::unique_ptr<Coffee> c) : inner(std::move(c)) {}
};
class MilkDecorator : public Decorator {
public:
using Decorator::Decorator;
int cost() const override { return inner->cost() + 3; }
};
int main() {
auto c = std::make_unique<MilkDecorator>(std::make_unique<BasicCoffee>());
std::cout << c->cost();
return 0;
}
Test yourself
-
Complete the Decorator constructor
explicit Decorator(std::unique_ptr<Coffee> ____) : inner(std::move(inner)) {}
Show answer
explicit Decorator(std::unique_ptr<Coffee> inner) : inner(std::move(inner)) {}
-
What does the Decorator pattern enable?
- Creating a single instance
- Hiding object creation
- Adding behavior without inheritance
- Swapping algorithms
Show answer
Adding behavior without inheritance — Decorator extends behavior without inheritance.
-
Match each piece to its role
- class Coffee
- Abstract component
- class BasicCoffee
- Concrete component
- class Decorator
- Wrapper base
- class MilkDecorator
- Concrete wrapper
Next lesson: Summary - Design patterns