Implementing Decorator Pattern in Java
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
To implement Decorator in Java, you need four pieces.
First, define a component interface with the methods you want to decorate. Say it's Coffee with cost and description methods. Second, create a concrete component that implements the interface—SimpleCoffee implements those methods with base behavior.
Third, make an abstract base decorator that also implements the interface. It holds a reference to another Coffee in a protected field, takes it via constructor, and delegates all methods to it by default. Fourth, create concrete decorators that extend the base decorator and override only the methods where they add behavior.
So MilkDecorator extends CoffeeDecorator, overrides cost to add point-five dollars, and overrides description to append comma-milk. Same with SugarDecorator. Each concrete decorator calls the wrapped method after adding behavior—that's the delegation chain.
To use it, you nest constructors: new SugarDecorator wrapping new MilkDecorator wrapping new SimpleCoffee. When you call cost on the outermost decorator, it adds its cost, then calls wrapped-dot-cost, which adds its cost, then calls wrapped-dot-cost, which returns the base cost. The whole chain executes.
The key parts are: the interface for substitutability, the base decorator for delegation boilerplate, and constructor injection of the wrapped component. One mistake is skipping the base decorator and duplicating delegation in every concrete decorator. Another is not implementing the interface—if your decorator extends SimpleCoffee directly, it's tightly coupled to that one implementation and won't work with other Coffee types.
Implementing Decorator Pattern in Java
Direct Answer
Define a component interface, create a concrete implementation, then build decorator classes that implement the interface, wrap a component instance via constructor injection, and add behavior before or after delegating to the wrapped object. An abstract base decorator handles the delegation boilerplate.
Code Example
// Step 1: Component interface
interface Coffee {
double cost();
String description();
}
// Step 2: Concrete component
class SimpleCoffee implements Coffee {
public double cost() { return 2.0; }
public String description() { return "Simple coffee"; }
}
// Step 3: Abstract base decorator (handles delegation)
abstract class CoffeeDecorator implements Coffee {
protected final Coffee wrapped; // Hold wrapped component
protected CoffeeDecorator(Coffee coffee) {
this.wrapped = coffee;
}
// Default delegation for all methods
public double cost() { return wrapped.cost(); }
public String description() { return wrapped.description(); }
}
// Step 4: Concrete decorators (override only what they enhance)
class MilkDecorator extends CoffeeDecorator {
MilkDecorator(Coffee coffee) { super(coffee); }
public double cost() { return wrapped.cost() + 0.5; } // Add milk cost
public String description() { return wrapped.description() + ", milk"; }
}
class SugarDecorator extends CoffeeDecorator {
SugarDecorator(Coffee coffee) { super(coffee); }
public double cost() { return wrapped.cost() + 0.2; } // Add sugar cost
public String description() { return wrapped.description() + ", sugar"; }
}
// Usage: compose at runtime
Coffee coffee = new SugarDecorator(
new MilkDecorator(
new SimpleCoffee()
)
);
System.out.println(coffee.description() + " = $" + coffee.cost());
// Output: Simple coffee, milk, sugar = $2.7
Common Mistakes & Pitfalls
- Skipping the base decorator: Freshers make each concrete decorator implement the interface directly, duplicating delegation code in every decorator—the abstract base decorator exists to eliminate this duplication by delegating once.
- Decorator not implementing interface: Creating a decorator that extends the concrete component instead of implementing the interface breaks substitutability—clients can't treat decorated and undecorated objects uniformly.
- Forgetting to call wrapped method: Adding behavior in a decorator but not delegating to the wrapped object means the original functionality is lost—always delegate unless you're intentionally replacing behavior.
- Exposing wrapped field: Making the wrapped field public or giving it a getter breaks encapsulation—clients shouldn't know or care that they're talking to a decorator, and accessing the wrapped object directly bypasses decorator behavior.
Check your understanding
Answer these without looking back at the article.
- What is the difference between before-decoration and after-decoration?
- How do you remove a decorator from the chain?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.