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 dynamically

Interactive Decorator Pattern SimulatorLIVE PLAYGROUND

Interact with the controls below to see how this design pattern executes in memory at runtime.

Category: Structural
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();
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

ParticipantRole & ResponsibilityType
Coffee (Component)Common interface defining cost() and description()interface
SimpleCoffee (Concrete Component)Base implementation ($2.00)class
CoffeeDecorator (Abstract Decorator)Maintains reference to wrapped Coffeeclass
MilkDecorator, CaramelDecoratorAdds cost and appends descriptionclass
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.