Decorator Pattern
Decorator Pattern
Attach additional responsibilities to an object dynamically — providing a flexible alternative to subclassing for extending functionality.
Intent
Decorator wraps a Component object (ConcreteComponent) with a Decorator that implements the same interface, delegating all calls to the wrapped object while adding behaviour before or after the delegation. Multiple decorators can be stacked; each layer adds one responsibility. This avoids the combinatorial subclass explosion that occurs when N features can be independently applied — Logger + Compressor + Encryptor would require 7 subclass combinations without Decorator, but only 3 decorator classes with it.
When NOT to Use
- The component interface is large — each Decorator must implement all methods, even those it does not modify; the boilerplate burden becomes prohibitive
- Only one or two responsibilities need to be added — subclassing or simple composition is clearer
- Order of decoration matters and must be enforced — a Builder constructing the chain explicitly gives more control
- The stack depth would exceed 4 layers — consider Pipes and Filters (see max-depth heuristic below)
When to Use
- Add responsibilities to individual objects without affecting other objects of the same class
- Responsibilities can be added or withdrawn at runtime
- Subclassing would produce a combinatorial explosion of variants (Logger + Compressor + Encryptor = 7 subclasses without Decorator)
How It Works
Participants: Component (interface), ConcreteComponent (base object being wrapped), Decorator (abstract, holds a Component reference, delegates all calls to it), ConcreteDecorator (extends Decorator, adds behaviour before/after delegation).
The Decorator implements the same interface as what it wraps — this is what allows transparent stacking. A client holding a Component reference cannot tell whether it holds the ConcreteComponent directly or a chain of Decorators wrapping it. The innermost object in the chain is always the ConcreteComponent; each outer layer adds one concern.
Class Diagram
classDiagram
class Component {
<<interface>>
+operation()*
}
class ConcreteComponent {
+operation()
}
class Decorator {
<<abstract>>
#wrapped : Component
+operation()
}
class ConcreteDecoratorA {
+operation()
+addedBehavior()
}
class ConcreteDecoratorB {
+operation()
+addedBehavior()
}
Component <|.. ConcreteComponent
Component <|.. Decorator
Decorator <|-- ConcreteDecoratorA
Decorator <|-- ConcreteDecoratorB
Decorator o-- Component : wraps
note for Decorator "Each decorator wraps a Component\nand can chain: D_B( D_A( Concrete ) )"
TypeScript Example
// Decorator — TypeScript (manual wrapper — the GoF structural pattern)
// Source: GoF Design Patterns, p.175
interface Logger {
log(message: string): void;
}
class ConsoleLogger implements Logger {
log(message: string) { console.log(message); }
}
class TimestampDecorator implements Logger {
constructor(private wrapped: Logger) {}
log(message: string) {
this.wrapped.log(`[${new Date().toISOString()}] ${message}`);
}
}
// Stack decorators: at most 3-4 deep (see max-depth heuristic)
const logger = new TimestampDecorator(new ConsoleLogger());
logger.log("hello");Java Example
// Decorator — Java (manual wrapper)
// Source: GoF Design Patterns, p.175
interface Logger { void log(String message); }
class ConsoleLogger implements Logger {
public void log(String msg) { System.out.println(msg); }
}
class TimestampDecorator implements Logger {
private final Logger wrapped;
TimestampDecorator(Logger l) { this.wrapped = l; }
public void log(String msg) {
wrapped.log("[" + Instant.now() + "] " + msg);
}
}Max-Depth Heuristic
Max-depth heuristic: More than 3-4 layers of Decorator wrapping is a warning sign. Deep chains make debugging hard (stack traces traverse every wrapper) and reading order unclear (innermost wrapper applies first). At 4+ layers, consider a Pipeline or Pipes-and-Filters approach (see EIP-Pipes-and-Filters-Pattern, created Phase 11) or consolidate related decorators into a single wrapper.
TypeScript Decorator Syntax Note
TypeScript Decorator Syntax Note: TypeScript's
@decoratorsyntax — legacyexperimentalDecorators: true(TS 4.x and earlier) and the TC39 Stage 3 proposal (TS 5.0+, different semantics) — is a language feature for class and member metadata. It shares the name "decorator" with this GoF structural pattern but differs in mechanism. The GoF Decorator is a structural composition pattern using manual wrapping; TypeScript's@decoratorsyntax is a compile-time transformation. Prefer the manual-wrapping approach shown above when the GoF pattern is the intent. Use@decoratorsyntax for cross-cutting concerns (logging, validation, serialisation) when the framework (NestJS, Angular) supports it.
Lineage Forward
Decorator → EIP-Pipes-and-Filters-Pattern (Phase 11) → Sidecar-Pattern (Phase 14). Each layer extends the same interface; Pipes and Filters generalises this to a processing pipeline.
Related Concepts
| Pattern | Relationship |
|---|---|
| Proxy-Pattern | Both wrap an object with the same interface; Decorator adds behaviour, Proxy controls access. See Decorator-vs-Proxy-vs-Adapter |
| Adapter-Pattern | Adapter changes the interface; Decorator preserves it. See Decorator-vs-Proxy-vs-Adapter |
| Composite-Pattern | Both use a component reference; Composite manages a tree, Decorator wraps a single object |
| EIP-Pipes-and-Filters-Pattern | Generalisation of Decorator stacking to a processing pipeline (created Phase 11) |
Related API Design Patterns
- gRPC-Service-Design — gRPC interceptors implement cross-cutting concerns by wrapping the call with the same interface — the Decorator structural pattern applied at the RPC layer
Related Frontend Patterns
- Component-Composition-Patterns — Higher-Order Components (HOC) in React are the direct application of the Decorator pattern to UI components: a HOC wraps a component with the same interface and adds cross-cutting behaviour (auth, analytics, error boundaries). Angular
@Directiveapplies the same structural pattern at the DOM element level.
Sources
- Gamma, Helm, Johnson, Vlissides — Design Patterns, Addison-Wesley, 1994, p.175
Backlinks
Notes that link here: GoF-Patterns-MOC, Decorator-vs-Proxy-vs-Adapter