Java Decorator Pattern
In this page:
What is the Decorator Pattern?
The Decorator pattern attaches additional responsibilities to an object dynamically by wrapping it in another object that implements the same interface, providing a flexible alternative to subclassing for extending behavior.
Example: What is the Decorator Pattern?
public class Main {
interface Coffee { String describe(); }
static class SimpleCoffee implements Coffee {
public String describe() { return "Coffee"; }
}
static class MilkDecorator implements Coffee {
Coffee wrapped;
MilkDecorator(Coffee wrapped) { this.wrapped = wrapped; }
public String describe() { return wrapped.describe() + " + Milk"; } // wraps, same interface
}
public static void main(String[] args) {
Coffee coffee = new MilkDecorator(new SimpleCoffee());
System.out.println(coffee.describe());
}
}
Login to try C/C++/Java/PHP code in the editor
Wrapping an Object
A decorator implements the same interface as the object it wraps, delegating to the wrapped object for its core behavior while adding its own logic before or after that delegated call.
Example: Wrapping an Object
public class Main {
interface Coffee { String describe(); }
static class SimpleCoffee implements Coffee {
public String describe() { return "Coffee"; }
}
static class SugarDecorator implements Coffee {
Coffee wrapped;
SugarDecorator(Coffee wrapped) { this.wrapped = wrapped; }
public String describe() { return wrapped.describe() + " + Sugar"; } // delegates, then adds
}
public static void main(String[] args) {
System.out.println(new SugarDecorator(new SimpleCoffee()).describe());
}
}
Login to try C/C++/Java/PHP code in the editor
Stacking Multiple Decorators
Multiple decorators can be layered on top of each other in any order or combination, since each one both implements and wraps the same interface, letting behavior be composed flexibly at runtime.
Example: Stacking Multiple Decorators
public class Main {
interface Coffee { String describe(); }
static class SimpleCoffee implements Coffee { public String describe() { return "Coffee"; } }
static class MilkDecorator implements Coffee {
Coffee wrapped; MilkDecorator(Coffee wrapped) { this.wrapped = wrapped; }
public String describe() { return wrapped.describe() + " + Milk"; }
}
static class SugarDecorator implements Coffee {
Coffee wrapped; SugarDecorator(Coffee wrapped) { this.wrapped = wrapped; }
public String describe() { return wrapped.describe() + " + Sugar"; }
}
public static void main(String[] args) {
Coffee coffee = new SugarDecorator(new MilkDecorator(new SimpleCoffee())); // layered, any order
System.out.println(coffee.describe());
}
}
Login to try C/C++/Java/PHP code in the editor
Decorator vs Inheritance
Using inheritance to add every combination of optional behavior requires a new subclass for each combination, quickly becoming unmanageable, while decorators combine independently without multiplying the number of classes needed.
Example: Decorator vs Inheritance
public class Main {
interface Coffee { String describe(); }
static class SimpleCoffee implements Coffee { public String describe() { return "Coffee"; } }
static class Decorator implements Coffee {
Coffee wrapped; String extra;
Decorator(Coffee wrapped, String extra) { this.wrapped = wrapped; this.extra = extra; }
public String describe() { return wrapped.describe() + " + " + extra; }
}
public static void main(String[] args) {
// One Decorator class combines freely, instead of MilkCoffee, SugarCoffee, MilkSugarCoffee subclasses...
Coffee coffee = new Decorator(new Decorator(new SimpleCoffee(), "Milk"), "Sugar");
System.out.println(coffee.describe());
}
}
Login to try C/C++/Java/PHP code in the editor
Java's Built-in Decorators
Java's own I/O classes are a well-known real-world example of the Decorator pattern: BufferedReader, BufferedWriter, and PrintWriter all wrap another Reader or Writer, adding buffering or formatting on top of it.
Example: Java's Built-in Decorators
import java.io.*;
public class Main {
public static void main(String[] args) throws Exception {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(new BufferedWriter(sw)); // both wrap and add formatting/buffering
pw.println("wrapped output");
pw.flush();
System.out.println(sw.toString().trim());
}
}
Login to try C/C++/Java/PHP code in the editor
Chapter Quiz — Complete all 14 topics to unlock
0/14 topics done
Complete these topics first:
- Java Design Patterns Introduction
- Java Singleton Pattern
- Java Factory Pattern
- Java Observer Pattern
- Java Builder Pattern
- Java MVC Architecture
- Java Adapter Pattern
- Java Decorator Pattern
- Java Strategy Pattern
- Java Command Pattern
- Java Facade Pattern
- Java Proxy Pattern
- Java Template Method Pattern
- Java Repository Pattern