Implementing the Decorator Pattern in C++ for Dynamic Function Extension
The Decorator pattern enables adding new responsibilities to an object dynamically without altering its structure. It creates a chain of decorators, each wrapping the previous object, forming a linked structure similar to a linked list. This approach maintains references to original functionality while introducing new behavior.
Consider a scenario where a person's outfit is assembled dynamically. Each clothing item decorates the person's base state, adding its own description while preserving the previous layers.
1. Base Component Interface
class OutfitComponent {
public:
virtual ~OutfitComponent() {}
virtual void display() const {
std::cout << "Basic outfit.\n";
}
};
2. Abstract Decorator Class
class ClothingDecorator : public OutfitComponent {
public:
ClothingDecorator() : wrappedComponent(nullptr) {}
virtual ~ClothingDecorator() {}
void display() const override {
if (wrappedComponent) {
wrappedComponent->display();
}
}
void wrap(OutfitComponent* component) {
wrappedComponent = component;
}
private:
OutfitComponent* wrappedComponent;
};
3. Concrete Decorator Implementations
class TShirtDecorator : public ClothingDecorator {
public:
void display() const override {
std::cout << "T-shirt ";
ClothingDecorator::display();
}
};
class JeansDecorator : public ClothingDecorator {
public:
void display() const override {
std::cout << "Jeans ";
ClothingDecorator::display();
}
};
Each concrete decorator outputs its own description before delegating to the wrapped component's display method, preserving the chain of functionality.
4. Assembling the Decorated Object
int main() {
OutfitComponent person;
TShirtDecorator shirt;
JeansDecorator jeans;
shirt.wrap(&person);
jeans.wrap(&shirt);
jeans.display();
return 0;
}
Execution flows from the outermost decorator inward: JeansDecorator::display() prints "Jeans", then calls TShirtDecorator::display() which prints "T-shirt", finally invoking the base OutfitComponent::display() that outputs "Basic outfit.".
5. Pattern Flexibiilty and Applications
The Decorator pattern supports runtime modification of object behavior. To change the outfit, simply create a new decorator and adjust the wrapping order. For instance, adding a JacketDecorator:
class JacketDecorator : public ClothingDecorator {
public:
void display() const override {
std::cout << "Jacket ";
ClothingDecorator::display();
}
};
This pattern is particularly effective for etxending functionality in layered systems. Consider a text processing system with a base text editor. Decorators can add features like syntax highlighting, spell checking, or auto-formatting. Each decorator performs its specific operation before or after delegating to the wrapped component, allowing flexible combination of features without modifying core classes.