Decorator
- C++ course
- Advanced
- Lesson 35/83
- 5 min
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.
Practice 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().
Starter code
#include <iostream>
#include <memory>
// Define the Coffee abstract class and BasicCoffee class
// Define the Decorator and MilkDecorator classes
int main() {
// Build a MilkDecorator(BasicCoffee) chain and print cost()
return 0;
}Sample 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;
}Expected output
13
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 Complete the Decorator constructor
explicit Decorator(std::unique_ptr<Coffee> ____) : inner(std::move(inner)) {}
explicit Decorator(std::unique_ptr<Coffee> inner) : inner(std::move(inner)) {}
Explanation: The Decorator keeps the wrapped object in the inner member.
2 What does the Decorator pattern enable?
- Creating a single instance
- Hiding object creation
- Adding behavior without inheritance Correct answer
- Swapping algorithms
Explanation: Decorator extends behavior without inheritance.
3 Match each piece to its role
| Term | Meaning |
|---|---|
class Coffee |
Abstract component |
class BasicCoffee |
Concrete component |
class Decorator |
Wrapper base |
class MilkDecorator |
Concrete wrapper |