Decorator Pattern
Attach responsibilities to an individual object dynamically by wrapping it with objects that implement the same interface.

Concept Overview
The Decorator pattern adds behaviour to an object at runtime without changing its class. A decorator implements the same interface as the wrapped component, holds a reference to it, and delegates while adding behaviour before, after or around the delegated call. Multiple decorators can be composed. This provides a flexible alternative to creating a subclass for every possible feature combination.
1. The problem: subclass combinations multiply
Suppose a Coffee class can be extended with milk, sugar and whipped cream. If each combination becomes a separate subclass, the number of classes grows quickly: MilkCoffee, SugarCoffee, MilkSugarCoffee and WhippedCreamCoffee. Adding new features or changing their combinations becomes difficult. Applying every feature to every instance is also too broad when only some objects need the extra behaviour.
2. Wrap the component with decorators
The Component defines the shared interface. The Concrete Component provides the base behaviour. The abstract Decorator holds a Component reference and implements the same interface. Concrete Decorators add specific responsibilities and delegate to the wrapped component. The client builds the desired combination by wrapping one object with one or more decorators.
interface Coffee {
String getDescription();
double getCost();
}
final class SimpleCoffee implements Coffee {
public String getDescription() { return "Coffee"; }
public double getCost() { return 50.0; }
}
abstract class CoffeeDecorator implements Coffee {
protected final Coffee coffee;
protected CoffeeDecorator(Coffee coffee) { this.coffee = coffee; }
}
final class MilkDecorator extends CoffeeDecorator {
MilkDecorator(Coffee coffee) { super(coffee); }
public String getDescription() { return coffee.getDescription() + ", Milk"; }
public double getCost() { return coffee.getCost() + 10.0; }
}3. Decorator composition is runtime composition
The client can start with SimpleCoffee, wrap it with MilkDecorator, then wrap that result with SugarDecorator. Each decorator still implements Coffee, so the next wrapper accepts it without knowing the concrete wrapper beneath it. The order of wrappers can matter if decorators transform data or surround operations, so composition order should be intentional and documented.
Coffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
System.out.println(coffee.getDescription());
System.out.println(coffee.getCost());4. Decorator and the Open/Closed Principle
Decorator allows new responsibilities to be added without modifying the existing concrete component. It is therefore a practical way to extend behaviour through composition. Java I/O streams use this style: buffered, data-conversion and other stream wrappers add behaviour around another stream. Frameworks also use decorator-like wrappers for transactions, caching or cross-cutting concerns, although each framework abstraction has its own details.
5. Advantages and drawbacks
Decorator gives fine-grained optional behaviour, avoids a large subclass matrix and allows features to be added or removed at runtime. Too many nested wrappers can make object identity and execution order difficult to understand. Debugging can require stepping through several layers. It is best when responsibilities are independent and composable, not when every feature requires complex coordination with every other feature.
Key Takeaways
- ✓A decorator implements the same interface as the component it wraps.
- ✓Responsibilities can be composed at runtime instead of encoded in subclass combinations.
- ✓Wrapper order can affect behaviour and should be deliberate.
- ✓Too many nested decorators can increase debugging and comprehension cost.