The Decorator pattern wraps an object in successive layers, each adding behavior before or after delegating to the wrapped object. All layers share the same interface, so the caller treats the chain identically to the original object.
Handler (interface)
└── BaseHandler — core logic
└── AuthDecorator — wraps a Handler, checks auth
└── LoggingDecorator — wraps a Handler, logs request/response
└── MetricDecorator — wraps a Handler, records metrics
Each decorator holds a wrappedHandler Handler field and calls it inside its own Handle() implementation.
var handler decorator.Handler = decorator.NewBaseHandler()
handler = decorator.NewAuthDecorator(handler)
handler = decorator.NewLoggingDecorator(handler)
handler = decorator.NewMetricDecorator(handler)
handler.Handle(decorator.Request{})Calling handler.Handle() on the outermost decorator triggers a chain:
MetricDecorator.Handle()
└── LoggingDecorator.Handle()
└── AuthDecorator.Handle()
└── BaseHandler.Handle()
Each decorator runs its own pre/post logic around the inner call.
- Every decorator must implement the same interface as the object it wraps.
- Decorators are composed at runtime, not compile time — the order matters.
- Adding or removing a cross-cutting concern requires no changes to other layers.
- You need to add responsibilities to objects without subclassing.
- Cross-cutting concerns (auth, logging, metrics, caching) should be applied independently and in combination.
- The number of feature combinations would make inheritance impractical.