Decorator
Intent
Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
Problem
You have a plain text component and want to optionally apply bold or italic formatting. Using subclasses for every combination (bold, italic, bold-italic) leads to a class explosion. You need a way to compose formatting behaviors at runtime.
Real-World Analogy
Think of ordering coffee. You start with a plain espresso, then add milk (latte), then add vanilla syrup, then add whipped cream. Each addition wraps the previous drink and adds something to it — but it’s still “a coffee” at every step. You can stack toppings in any order, and the price is the sum of all layers. Each topping is a decorator.
When You Need It
- You’re building a stream processing pipeline where data can be compressed, encrypted, and buffered — and users should be able to combine these in any order at runtime
- You’re adding logging, caching, or retry logic to API calls without modifying the original service class — just wrap it with decorators
- You’re creating a text rendering system where text can be bold, italic, underlined, or any combination — and you don’t want a class for every possible combination
UML Class Diagram
classDiagram
class ITextComponent {
<<interface>>
+Render() string
}
class PlainText {
-text: string
+Render() string
}
class TextDecorator {
<<abstract>>
#wrapped: ITextComponent
+Render() string
}
class BoldDecorator {
+Render() string
}
class ItalicDecorator {
+Render() string
}
ITextComponent <|.. PlainText
ITextComponent <|.. TextDecorator
TextDecorator <|-- BoldDecorator
TextDecorator <|-- ItalicDecorator
TextDecorator --> ITextComponent
Sequence Diagram
sequenceDiagram
Client->>ConcreteDecorator: operation()
ConcreteDecorator->>Decorator: operation()
Decorator->>Component: operation()
Component-->>Decorator: result
Decorator-->>ConcreteDecorator: wrapped result
ConcreteDecorator-->>Client: decorated result
Participants
| Participant | Role |
|---|---|
ITextComponent |
Component – defines the interface for objects that can have responsibilities added. |
PlainText |
ConcreteComponent – the object to which additional responsibilities can be attached. |
TextDecorator |
Decorator – maintains a reference to a Component and conforms to its interface. |
BoldDecorator, ItalicDecorator |
ConcreteDecorator – add responsibilities to the component. |
How It Works
PlainTextreturns raw text fromRender().BoldDecoratorwraps a component, calls itsRender(), and surrounds the result with bold tags.ItalicDecoratordoes the same with italic tags.- Decorators can be stacked: wrapping a
PlainTextinBoldDecoratorthenItalicDecoratorproduces bold-italic text.
Applicability
- You want to add responsibilities to individual objects dynamically and transparently.
- You want to avoid a class explosion from subclassing every combination of features.
- Extension by subclassing is impractical.
Trade-offs
Pros:
- More flexible than static inheritance — behaviors can be added and removed at runtime
- Avoids a class explosion from every possible combination of features
- Each decorator has a single responsibility, keeping classes small and focused
Cons:
- Many small decorator classes can make the codebase harder to navigate
- Deeply nested decorators are hard to debug — the call stack gets long
- Order of wrapping matters and can introduce subtle bugs
Example Code
C#
public interface ITextComponent
{
string Render();
}
public class PlainText : ITextComponent
{
private readonly string _text;
public PlainText(string text) => _text = text;
public string Render() => _text;
}
public abstract class TextDecorator : ITextComponent
{
protected readonly ITextComponent Wrapped;
protected TextDecorator(ITextComponent wrapped) => Wrapped = wrapped;
public abstract string Render();
}
public class BoldDecorator : TextDecorator
{
public BoldDecorator(ITextComponent wrapped) : base(wrapped) {}
public override string Render() => $"<b>{Wrapped.Render()}</b>";
}
public class ItalicDecorator : TextDecorator
{
public ItalicDecorator(ITextComponent wrapped) : base(wrapped) {}
public override string Render() => $"<i>{Wrapped.Render()}</i>";
}
Delphi
type
ITextComponent = interface
function Render: string;
end;
TPlainText = class(TInterfacedObject, ITextComponent)
private
FText: string;
public
constructor Create(const AText: string);
function Render: string;
end;
TTextDecorator = class(TInterfacedObject, ITextComponent)
protected
FWrapped: ITextComponent;
public
constructor Create(AWrapped: ITextComponent);
function Render: string; virtual; abstract;
end;
TBoldDecorator = class(TTextDecorator)
public
function Render: string; override;
end;
TItalicDecorator = class(TTextDecorator)
public
function Render: string; override;
end;
constructor TPlainText.Create(const AText: string);
begin
FText := AText;
end;
function TPlainText.Render: string;
begin
Result := FText;
end;
constructor TTextDecorator.Create(AWrapped: ITextComponent);
begin
FWrapped := AWrapped;
end;
function TBoldDecorator.Render: string;
begin
Result := '<b>' + FWrapped.Render + '</b>';
end;
function TItalicDecorator.Render: string;
begin
Result := '<i>' + FWrapped.Render + '</i>';
end;
C++
#include <string>
#include <memory>
class ITextComponent {
public:
virtual ~ITextComponent() = default;
virtual std::string Render() = 0;
};
class PlainText : public ITextComponent {
std::string text_;
public:
PlainText(std::string text) : text_(std::move(text)) {}
std::string Render() override { return text_; }
};
class TextDecorator : public ITextComponent {
protected:
std::shared_ptr<ITextComponent> wrapped_;
public:
TextDecorator(std::shared_ptr<ITextComponent> wrapped)
: wrapped_(std::move(wrapped)) {}
};
class BoldDecorator : public TextDecorator {
public:
using TextDecorator::TextDecorator;
std::string Render() override {
return "<b>" + wrapped_->Render() + "</b>";
}
};
class ItalicDecorator : public TextDecorator {
public:
using TextDecorator::TextDecorator;
std::string Render() override {
return "<i>" + wrapped_->Render() + "</i>";
}
};
Runnable Examples
| Language | Source |
|---|---|
| C# | Decorator.cs |
| C++ | decorator.cpp |
| Delphi | decorator.pas |