Back to All Design Patterns
Pattern 6 of 22
Structural PatternINTERMEDIATE8 min deep dive
Decorator Pattern Pattern
Attaches additional responsibilities to an object dynamically, providing a flexible alternative to subclassing.
Real-World Mental Model / Metaphor:
Putting on clothes in cold weather. You put on a base shirt, then wrap yourself in a warm sweater, then wrap a raincoat over that. Each layer adds a behavior without altering your core body.
Interactive Runtime Simulator
Test and tweak inputs dynamicallyInteractive Decorator Pattern SimulatorLIVE PLAYGROUND
Interact with the controls below to see how this design pattern executes in memory at runtime.
1. Select Base Component:2. Wrap with Dynamic Decorators:
DYNAMIC DECORATOR WRAPPER STACKTotal: $3.75
Active Object Wrapping:
Dark Roast Espresso + Steamed Oat Milk + Caramel Drizzle
// Recursive Decorator Composition:
Beverage drink = new Espresso();
Beverage drink = new Espresso();
drink = new MilkDecorator(drink);
drink = new CaramelDecorator(drink);
System.out.println(drink.getCost()); // $3.75 The Problem It Solves
Class explosion caused by subclassing every combination of features (e.g. `EspressoWithMilkAndCaramel`, `EspressoWithWhip`, `EspressoWithMilkAndWhip...`).
The Architectural Solution
Wrap objects in decorator classes that implement the same interface and delegate calls to the wrapped instance while adding extra behavior.
Pattern Structure & Participants
| Participant | Role & Responsibility | Type |
|---|---|---|
| Coffee (Component) | Common interface defining cost() and description() | interface |
| SimpleCoffee (Concrete Component) | Base implementation ($2.00) | class |
| CoffeeDecorator (Abstract Decorator) | Maintains reference to wrapped Coffee | class |
| MilkDecorator, CaramelDecorator | Adds cost and appends description | class |
Collaboration Rules:
- CoffeeDecorator implements Coffee and contains a Coffee instance
- Calls cascade recursively down the decorator chain
Production Code Implementation
Decorator Pattern.java
// 1. Component Interface
public interface Beverage {
String getDescription();
double getCost();
}
// 2. Concrete Component
public class Espresso implements Beverage {
public String getDescription() { return "Dark Roast Espresso"; }
public double getCost() { return 2.50; }
}
// 3. Base Decorator
public abstract class CondimentDecorator implements Beverage {
protected final Beverage wrappedBeverage;
public CondimentDecorator(Beverage beverage) {
this.wrappedBeverage = beverage;
}
}
// 4. Concrete Decorators
public class Milk extends CondimentDecorator {
public Milk(Beverage beverage) { super(beverage); }
public String getDescription() { return wrappedBeverage.getDescription() + " + Steamed Milk"; }
public double getCost() { return wrappedBeverage.getCost() + 0.50; }
}
public class CaramelDrizzle extends CondimentDecorator {
public CaramelDrizzle(Beverage beverage) { super(beverage); }
public String getDescription() { return wrappedBeverage.getDescription() + " + Sweet Caramel"; }
public double getCost() { return wrappedBeverage.getCost() + 0.75; }
}
// Usage:
// Beverage myDrink = new CaramelDrizzle(new Milk(new Espresso()));
// Cost: $2.50 + $0.50 + $0.75 = $3.75! When to Use:
- •When you want to add responsibilities to individual objects dynamically and transparently without affecting other objects.
- •When extension by subclassing is impractical or causes combinatorial explosion.
- •When you need to withdraw or re-order responsibilities dynamically at runtime.
When to Avoid (Anti-Pattern):
- •When the component interface has dozens of methods (decorators must pass-through all of them).
- •When code relies on strict `instanceof` checks on the concrete underlying type.
Staff Engineering Interview Insights & Gotchas
- Compare Decorator vs Proxy: Decorator enhances behavior and interface composition; Proxy controls access or manages lifecycle.
- Show how `java.io.BufferedReader` wrapping `FileReader` is the canonical JDK decorator.