← Back to Advanced Java Course | Chapter 6: Design Patterns | Lesson 8 of 14

Java Decorator Pattern

The Decorator pattern attaches additional responsibilities to an object dynamically by wrapping it in another object implementing the same interface, offering a flexible alternative to subclassing.

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?

java
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());
	}
}

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

java
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());
	}
}

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

java
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());
	}
}

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

java
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());
	}
}

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

java
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 run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.