The Decorator Design Pattern is a structural pattern that lets you dynamically add new behavior or responsibilities to objects without modifying their underlying code.
It’s particularly useful in situations where:
if-else
logic for optional features.Let’s walk through a real-world example to see how we can apply the Decorator Pattern to build a modular, flexible, and easily composable system for enhancing object functionality.
Imagine you’re building a rich text rendering system (like a simple word processor or a markdown preview tool). At the core of your system is a TextView
component that renders plain text on screen.
Soon, product requirements evolve:
You might start by creating a base class like this:
And then extend it to support new features:
Bold Text View
Italic Text View
BoldItalicTextView
For every new combination of features, you need to create a new subclass:
BoldTextView
, ItalicTextView
, UnderlineTextView
BoldItalicTextView
, BoldUnderlineTextView
, ItalicUnderlineTextView
This doesn’t scale. With just 4 features, you'd already have to manage up to 15 classes for all possible combinations.
You can't dynamically change features at runtime. Want to turn bold on or off based on user preference? You’ll need conditional logic or object swapping.
Every time a new feature (like highlight
or shadow
) is introduced, you need to modify or extend existing classes — which increases the chance of regressions.
We need a solution that lets us:
This is exactly where the Decorator Pattern shines.
The Decorator Pattern allows you to add responsibilities to objects dynamically, without altering their structure or modifying their original code.
At its core, the pattern relies on wrapping an object inside another object (a decorator) that implements the same interface and adds new behavior before or after delegating to the wrapped object.
This creates a layered effect, where decorators can be stacked to apply multiple enhancements without creating a complex inheritance tree.
TextView
): Declares the common interface (execute()
) for all core and decorated objectsPlainTextView
): The base object that can be dynamically decoratedBoldDecorator
, ItalicDecorator
, etc.): Extend the base decorator to add new functionality before/after calling the wrapped component’s methodThink of a plain coffee. Now add milk. Now add sugar.
Each addition enhances the original but doesn’t change the base. The Decorator Pattern works the same way — stacking behaviors while keeping the core intact.
Let’s implement the Decorator Pattern to enable flexible styling of text elements — such as bold, italic, underline, and combinations of these — all without subclassing or modifying the core text rendering logic.
This interface will be used by both the base component and all decorators. It defines the render()
method that displays the text.
This is the base class that renders plain text. It implements the TextView
interface and contains the core content to be displayed.
This class also implements TextView
and holds a reference to another TextView
component. It forwards calls to the wrapped component.
Each decorator adds a specific formatting layer before or after calling the wrapped component's render()
method.
The client composes the desired formatting by wrapping decorators dynamically at runtime. This avoids subclass explosion and provides full control over order and combination.