← 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.
Syntax
java
class Decorator implements Component {
    private Component wrapped;
    public void operation() {
        wrapped.operation();
        // extra behavior
    }
}

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());
	}
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Forgetting to call the wrapped object's method inside the decorator, so the original behavior is lost.
  2. Making the decorator implement a different interface than the object it wraps, so it cannot be stacked with other decorators.
  3. Ignoring that the order of stacking matters, so wrapping in a different order gives a different result.

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.