Decorator Pattern
Attach additional responsibilities to an object dynamically without modifying its structure.
Intent & Objective
Provide a flexible alternative to subclassing for extending functionality dynamically by placing the target object inside special wrapper objects that execute their own behavior before or after delegating.
"You start with plain espresso. You can decorate it with steamed milk, vanilla syrup, or whipped cream. Each topping decorates the beverage dynamically without baking 50 hardcoded coffee classes."
"Wearing a base shirt, adding a sweater for chill, and topping with a waterproof raincoat for heavy rain. Layers wrap you dynamically without bodily modification."
"A baked sponge cake is wrapped in chocolate ganache, sprinkled with nuts, and topped with celebratory candles."
"Wrapping a core domain function with audit logging, performance timing, and parameter validation without touching the inner algorithm."
The Problem
Class explosion: if you have Coffee and want MilkCoffee, SugarCoffee, WhipCoffee, MilkSugarCoffee, MilkWhipCoffee, etc., subclassing requires (2^n) classes.
The Solution
Wrap the base object in decorator classes implementing the same interface. Decorators delegate to the inner object and add their own behavior.
Key Participants & Architectural Roles
Code Samples (Traditional OOP vs Functional Programming)
Available in C++, Python, and JavaParadigm Contrast: Traditional OOP vs Functional / Data-Oriented
In OOP, Decorator wraps objects in concentric classes. In Functional Programming, Decorator is literally function composition or higher-order function decoration: `decorated = f . g . h`.
Advantages
- ✓ Extends object behavior without making a new subclass
- ✓ Allows adding or removing responsibilities dynamically at runtime
- ✓ Can combine several behaviors by wrapping an object into multiple decorators
Trade-offs
- ✗ Hard to remove a specific wrapper from the middle of the wrapper stack
- ✗ Initial decorator configuration code can look verbose
When to Apply
- → When you need to assign extra behaviors to objects at runtime without breaking code that uses these objects
- → When it is awkward or impossible to extend an object’s behavior using inheritance
When to Avoid
- ! When the number of combinations is tiny and fixed