Decorator Pattern in Java
- Decorator adds behaviour at runtime by wrapping an object that implements the same interface.
- The key structure: decorator holds a reference to the wrapped object and delegates to it.
- Decorators compose freely — wrap N decorators around one component for N behaviours.
The Decorator pattern adds behaviour to an object at runtime without modifying its class. A decorator wraps another object of the same interface, calls the wrapped object's methods, and adds extra logic before or after. Java's I/O library (BufferedReader wrapping FileReader, GZIPInputStream wrapping FileInputStream) is the canonical real-world example.
Building a Decorator — Step by Step
package io.thecodeforge.java.patterns; // Step 1: Define the component interface interface TextProcessor { String process(String text); } // Step 2: Concrete component class PlainTextProcessor implements TextProcessor { @Override public String process(String text) { return text; } } // Step 3: Abstract decorator — holds a reference to the wrapped component abstract class TextProcessorDecorator implements TextProcessor { protected final TextProcessor wrapped; TextProcessorDecorator(TextProcessor wrapped) { this.wrapped = wrapped; } } // Step 4: Concrete decorators class UpperCaseDecorator extends TextProcessorDecorator { UpperCaseDecorator(TextProcessor wrapped) { super(wrapped); } @Override public String process(String text) { return wrapped.process(text).toUpperCase(); // add before/after } } class TrimDecorator extends TextProcessorDecorator { TrimDecorator(TextProcessor wrapped) { super(wrapped); } @Override public String process(String text) { return wrapped.process(text.trim()); // modify input } } class PrefixDecorator extends TextProcessorDecorator { private final String prefix; PrefixDecorator(TextProcessor wrapped, String prefix) { super(wrapped); this.prefix = prefix; } @Override public String process(String text) { return prefix + wrapped.process(text); } } public class DecoratorDemo { public static void main(String[] args) { // Compose freely at runtime TextProcessor simple = new PlainTextProcessor(); TextProcessor trimmed = new TrimDecorator(simple); TextProcessor upper = new UpperCaseDecorator(trimmed); TextProcessor tagged = new PrefixDecorator(upper, "[RESULT] "); System.out.println(tagged.process(" hello world ")); // [RESULT] HELLO WORLD } }
Java I/O — The Real-World Decorator Example
package io.thecodeforge.java.patterns; import java.io.*; import java.util.zip.GZIPInputStream; public class IODecorators { public static void main(String[] args) throws Exception { // Each wrapper adds behaviour without modifying the inner class: // FileReader: reads characters from a file // BufferedReader: adds buffering (reads in chunks, not char by char) // — both implement Reader try (BufferedReader reader = new BufferedReader( new FileReader("data.txt"))) { String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } // Stack more decorators for more behaviour: // FileInputStream → GZIPInputStream → InputStreamReader → BufferedReader try (BufferedReader gzipReader = new BufferedReader( new InputStreamReader( new GZIPInputStream( new FileInputStream("data.gz"))))) { System.out.println(gzipReader.readLine()); } // No class was modified — behaviour added by wrapping } }
🎯 Key Takeaways
- Decorator adds behaviour at runtime by wrapping an object that implements the same interface.
- The key structure: decorator holds a reference to the wrapped object and delegates to it.
- Decorators compose freely — wrap N decorators around one component for N behaviours.
- Java's I/O library (BufferedReader, GZIPInputStream) is the most-used Decorator in the JDK.
- Prefer Decorator over inheritance when you need combinations of optional behaviours.
Interview Questions on This Topic
- QExplain the Decorator pattern and give a real Java example.
- QWhat problem does the Decorator pattern solve that inheritance cannot?
- QHow is Java's BufferedReader an example of the Decorator pattern?
Frequently Asked Questions
What is the difference between Decorator and Inheritance?
Inheritance adds behaviour at compile time — you choose the subclass when writing code. Decorator adds behaviour at runtime — you choose the wrappers when creating objects. Inheritance creates an explosion of subclasses for combinations; Decorator creates a few wrappers that combine freely.
What is the difference between Decorator and Proxy?
Both wrap an object of the same interface. Decorator adds new behaviour — it extends what the object does. Proxy controls access — it may restrict, delay, cache, or log access to the underlying object without fundamentally changing its behaviour. The distinction is intent.
Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.