CodeBlanks Logo Icon CodeBlanks

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.

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

main.cpp
#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

main.cpp
#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

Question
explicit Decorator(std::unique_ptr<Coffee> ____) : inner(std::move(inner)) {}
Answer
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

All lessons in Advanced