Imagine you need to display text with various formatting options, like bold or italic, but you want to keep the core text-display logic unchanged. Modifying the base class for each new style can quickly become messy and violate the Open-Closed Principle. How do we add functionality dynamically without altering existing code? The Decorator Pattern is the answer!
Solution: The Decorator Pattern
The Decorator Pattern allows us to wrap a core component with additional responsibilities dynamically. In Java, this is achieved using an interface, a base component, and decorator classes that implement the same interface while holding a reference to the component they enhance. This enables stacking behaviors without modifying the original object.
Below is a practical example in Java demonstrating the Decorator Pattern to add text formatting.
// Component Interface
interface Text {
String display();
}
// Concrete Component
class SimpleText implements Text {
private String content;
public SimpleText(String content) {
this.content = content;
}
@Override
public String display() {
return content;
}
}
// Abstract Decorator Class
abstract class TextDecorator implements Text {
protected Text decoratedText;
public TextDecorator(Text decoratedText) {
this.decoratedText = decoratedText;
}
@Override
public String display() {
return decoratedText.display();
}
}
// Concrete Decorator: Bold
class BoldTextDecorator extends TextDecorator {
public BoldTextDecorator(Text decoratedText) {
super(decoratedText);
}
@Override
public String display() {
return "<b>" + super.display() + "</b>";
}
}
// Concrete Decorator: Italic
class ItalicTextDecorator extends TextDecorator {
public ItalicTextDecorator(Text decoratedText) {
super(decoratedText);
}
@Override
public String display() {
return "<i>" + super.display() + "</i>";
}
}
// Demo Class
public class DecoratorPatternDemo {
public static void main(String[] args) {
Text simpleText = new SimpleText("Hello, World!");
Text boldText = new BoldTextDecorator(simpleText);
Text italicText = new ItalicTextDecorator(simpleText);
Text boldItalicText = new BoldTextDecorator(new ItalicTextDecorator(simpleText));
System.out.println("Plain Text: " + simpleText.display());
System.out.println("Bold Text: " + boldText.display());
System.out.println("Italic Text: " + italicText.display());
System.out.println("Bold and Italic Text: " + boldItalicText.display());
}
}
The Text interface defines the core operation (display()).
SimpleText is the concrete component that holds the base text.
TextDecorator is an abstract class that implements Text and holds a reference to a Text object via its constructor.
Concrete decorators (BoldTextDecorator, ItalicTextDecorator) extend TextDecorator to add specific formatting.
The main method shows how decorators can be stacked to combine behaviors (e.g., bold + italic).
Output:
Plain Text: Hello, World!
Bold Text: <b>Hello, World!</b>
Italic Text: <i>Hello, World!</i>
Bold and Italic Text: <b><i>Hello, World!</i></b>
Conclusion
The Decorator Pattern solves the problem of adding responsibilities to objects flexibly and reusably. By using an interface and passing the component through the decorator’s constructor, we can stack behaviors dynamically without modifying the core class. This approach adheres to the Open-Closed Principle and makes the system extensible for future enhancements, like adding new decorators for underlining or colors. Try it out in your Java projects to keep your code clean and modular!
Top comments (0)