C++ Decorator Pattern Example

1. Definition

The Decorator Design Pattern attaches additional responsibilities or behaviors to an object dynamically. It provides a flexible alternative to subclassing for extending functionality.

2. Problem Statement

How can we add additional responsibilities to individual objects without affecting other objects and without resorting to a monolithic class that encapsulates all possible functionalities?

3. Solution

By wrapping the original object with new objects having additional behaviors. These wrappers (decorators) mimic the interface of the object they decorate but add or override behaviors.

4. Real-World Use Cases

1. Adding window dressing (e.g., scroll bars, borders) to graphical windows.

2. Java's I/O Streams, which wrap streams to add features (e.g., buffering, character encoding/decoding).

3. Middleware in web frameworks that add behaviors like logging, caching, etc.

5. Implementation Steps

1. Define a base component interface.

2. Create concrete component classes that implement this interface.

3. Create decorator classes that also implement the component interface and have a reference to a component object.

4. In decorator methods, call the method on the referenced component and add extra behaviors.

6. Implementation in C++

// Step 1: Define the Component interface
class Component {
public:
    virtual ~Component() {}
    virtual void operation() const = 0;
};
// Step 2: Create concrete component
class ConcreteComponent : public Component {
public:
    void operation() const {
        std::cout << "Basic Operation\n";
    }
};
// Step 3: Create Base Decorator
class Decorator : public Component {
protected:
    Component* component;
public:
    Decorator(Component* comp) : component(comp) {}
    virtual void operation() const {
        component->operation();
    }
};
// Concrete Decorator A
class ConcreteDecoratorA : public Decorator {
public:
    ConcreteDecoratorA(Component* comp) : Decorator(comp) {}
    void operation() const {
        Decorator::operation();
        std::cout << "Decorator A Operation\n";
    }
};
// Concrete Decorator B
class ConcreteDecoratorB : public Decorator {
public:
    ConcreteDecoratorB(Component* comp) : Decorator(comp) {}
    void operation() const {
        Decorator::operation();
        std::cout << "Decorator B Operation\n";
    }
};
int main() {
    Component* simple = new ConcreteComponent();
    Component* decoratorA = new ConcreteDecoratorA(simple);
    Component* decoratorB = new ConcreteDecoratorB(decoratorA);
    decoratorB->operation();
    delete simple;
    delete decoratorA;
    delete decoratorB;
    return 0;
}

Output:

Basic Operation
Decorator A Operation
Decorator B Operation

Explanation:

1. Component defines the interface for objects that can have responsibilities added dynamically.

2. ConcreteComponent is a basic object we wish to add behaviors to.

3. Decorator is the base class for all decorators. It maintains a reference to a Component and defines an interface that conforms to Component's interface.

4. ConcreteDecoratorA and ConcreteDecoratorB add behaviors to the Component. When their operation is called, they first delegate the call to the underlying Component and then execute their added behavior.

5. In the main function, we create a basic component, wrap it in ConcreteDecoratorA, then wrap the result in ConcreteDecoratorB. Calling operation on the final object triggers the cascade of operations.

7. When to use?

Use the Decorator pattern when:

1. You need to add responsibilities to individual objects dynamically and transparently, without affecting other objects.

2. Extending functionalities by subclassing is impractical or cumbersome.

3. You want to keep new functionalities separate, with specific classes that encapsulate a specific feature.


Comments