Decorator
Intent
Attaches new behaviours to objects by placing them inside wrapper objects that share the same interface.
Problem
Users want notifications by email, SMS and Slack in any combination. Subclassing every combination explodes.
Solution
Wrap the base notifier in decorators. Each adds its behaviour and delegates to the wrapped object.
Real-world analogy
Dressing in layers: a sweater when it's cold, a jacket over it, a raincoat on top when it rains. Each layer adds something and can come off.
Structure
Hover or tap a numbered role to highlight it in the diagram.
- 1Component — Component declares the interface shared by wrappers and wrapped objects.
- 2ConcreteComponent — Concrete Component is the object being wrapped; it defines the basic behaviour.
- 3BaseDecorator — Base Decorator holds a reference to a wrapped Component and delegates all work to it.
- 4ConcreteDecorator — Concrete Decorators add behaviour before or after calling the parent method.
- 5Client — Client wraps components in as many layers as it needs.
Try it
Code example
// HTTP middleware is Go's everyday Decorator.
func Logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
})
}
// http.Handle("/", Logging(myHandler))Applicability
Use Decorator when adding behaviour at runtime without changing the object's code.
Use Decorator when inheritance is awkward or impossible (final classes).
How to implement
- Make sure the domain can be a primary component with optional layers
- Define a component interface
- Create a concrete component
- Create a base decorator holding a wrapped component and delegating to it
- Create concrete decorators that add behaviour before or after delegating
- Let the client compose decorators
Pros and cons
Pros
- Extend behaviour without subclassing
- Combine behaviours by stacking wrappers
- Single Responsibility per wrapper
Cons
- Hard to remove a specific wrapper from the stack
- Order of wrappers matters
- Many small objects can be hard to debug
Relations with other patterns
Check yourself
Add compression and then encryption around a file stream, in any combination, at runtime.
Saved in this browser only. Part of Structural patterns (0/7), Hands-on: patterns with live demos (0/5).