DEV Community

Cover image for The Decorator Pattern in Java
William
William

Posted on

The Decorator Pattern in Java

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());
    }
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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)