Decorator Pattern in Java: What It Is and How It Works
Decorator Pattern in Java is a design pattern that lets you add new behavior to objects dynamically without changing their original code. It wraps an object inside another object to extend its functionality while keeping the same interface.How It Works
Imagine you have a plain coffee, and you want to add milk or sugar without changing the coffee itself. The decorator pattern works similarly by wrapping an object with another object that adds new features. This wrapping can be done many times, each adding its own behavior.
In Java, this means you create a base interface or class, then create decorators that implement the same interface and hold a reference to the original object. When you call a method, the decorator can add extra steps before or after delegating the call to the original object.
This approach helps keep code flexible and easy to extend without modifying existing classes, which is great for maintaining and scaling software.
Example
This example shows a simple text message that can be decorated to add stars or brackets around it dynamically.
interface Message { String getContent(); } class SimpleMessage implements Message { private final String content; public SimpleMessage(String content) { this.content = content; } @Override public String getContent() { return content; } } class MessageDecorator implements Message { protected final Message message; public MessageDecorator(Message message) { this.message = message; } @Override public String getContent() { return message.getContent(); } } class StarDecorator extends MessageDecorator { public StarDecorator(Message message) { super(message); } @Override public String getContent() { return "*** " + super.getContent() + " ***"; } } class BracketDecorator extends MessageDecorator { public BracketDecorator(Message message) { super(message); } @Override public String getContent() { return "[" + super.getContent() + "]"; } } public class DecoratorDemo { public static void main(String[] args) { Message message = new SimpleMessage("Hello"); System.out.println(message.getContent()); Message starMessage = new StarDecorator(message); System.out.println(starMessage.getContent()); Message bracketStarMessage = new BracketDecorator(starMessage); System.out.println(bracketStarMessage.getContent()); } }
When to Use
Use the decorator pattern when you want to add features to objects at runtime without changing their code. It is helpful when subclassing would create too many classes or when you want to combine behaviors flexibly.
Real-world uses include adding scrollbars or borders to windows in graphical user interfaces, adding responsibilities like logging or security checks to objects, or formatting output dynamically.
Key Points
- The decorator pattern wraps an object to add new behavior.
- It keeps the same interface so wrapped objects can be used interchangeably.
- It helps avoid large inheritance trees by composing behaviors.
- Decorators can be stacked to combine multiple features.