On this page
Decorator
The Decorator pattern attaches additional responsibilities to an object dynamically, providing a flexible alternative to subclassing.
Problem
You need to add behavior to individual objects without affecting others, and subclassing leads to an explosion of classes.
Solution
interface Coffee {
String getDescription();
double getCost();
}
class SimpleCoffee implements Coffee {
public String getDescription() { return "Simple coffee"; }
public double getCost() { return 2.0; }
}
abstract class CoffeeDecorator implements Coffee {
protected final Coffee wrapped;
CoffeeDecorator(Coffee coffee) { this.wrapped = coffee; }
}
class MilkDecorator extends CoffeeDecorator {
MilkDecorator(Coffee coffee) { super(coffee); }
public String getDescription() { return wrapped.getDescription() + ", milk"; }
public double getCost() { return wrapped.getCost() + 0.5; }
}
class SugarDecorator extends CoffeeDecorator {
SugarDecorator(Coffee coffee) { super(coffee); }
public String getDescription() { return wrapped.getDescription() + ", sugar"; }
public double getCost() { return wrapped.getCost() + 0.2; }
}
// Usage — stack decorators
Coffee coffee = new SugarDecorator(new MilkDecorator(new SimpleCoffee()));
System.out.println(coffee.getDescription()); // Simple coffee, milk, sugar
System.out.println(coffee.getCost()); // 2.7
Java I/O Decorators
The classic Java example — I/O streams are decorators:
InputStream input = new BufferedInputStream(
new GZIPInputStream(
new FileInputStream("data.gz")
)
);
// BufferedInputStream decorates GZIPInputStream decorates FileInputStream
Each wrapper adds behavior: file access → decompression → buffering.
Decorator vs Inheritance
| Decorator | Inheritance |
|---|---|
| Runtime composition | Compile-time extension |
| Stack multiple behaviors | One level of extension |
| Same interface | Subclass interface |
| Flexible | Rigid |
Java Examples
Collections.synchronizedList(list); // thread-safe decorator
Collections.unmodifiableList(list); // read-only decorator
new BufferedReader(new FileReader(f)); // buffering decorator
When to Use
- Add responsibilities to objects individually and dynamically
- Subclassing would produce too many combinations
- You want to wrap objects with cross-cutting concerns (logging, caching)
Best Practices
- Keep the decorator interface identical to the wrapped object
- Order of decorators may matter — document dependencies
- Consider whether AOP (Spring
@Aspect) is cleaner for cross-cutting concerns - Do not over-decorate — deep chains are hard to debug