Decorator
Decorator wraps an object in another sharing its interface to add behavior dynamically, a stackable alternative to subclassing. It enables composable features but multiplies small wrapper classes.
The Decorator pattern attaches additional responsibilities to an object dynamically by wrapping it in another object that shares the same interface. It is a flexible alternative to subclassing for extending behavior. The problem it solves: when you want optional, combinable features, inheritance forces a separate subclass for every combination, which explodes quickly. Decorators let you stack features at runtime instead.
How It Works
A Component interface defines the operations. A Concrete Component is the base object being decorated. A Decorator also implements the Component interface and holds a reference to a wrapped Component; it forwards calls to the wrapped object and adds behavior before, after, or around the delegation. Concrete Decorators add specific responsibilities.
Because a decorator both implements and wraps the same interface, decorators can be stacked: each wraps another, forming a chain. The outermost object is still a Component, so clients use it identically to the bare object, unaware of how many layers sit beneath.
When to Use It
Use Decorator to add responsibilities to individual objects dynamically and transparently, without affecting other objects, and when extension by subclassing is impractical because of a combinatorial number of independent features. Canonical uses include I/O streams (buffering, compression, encryption layered over a base stream), HTTP middleware, and UI components with optional borders, scrollbars, or shadows.
It is less suitable when behavior depends on the object's concrete type or when the wrapped object's identity matters, since decorators change identity and can complicate equality checks and debugging.
Trade-offs
Decorator offers far more flexibility than static inheritance: features can be combined in any order at runtime, each in its own small, single-responsibility class, honoring the open/closed principle. You can add and remove responsibilities without touching existing code.
The costs are a proliferation of small wrapper classes and the difficulty of reasoning about a deep stack of decorators — the order of wrapping can matter, and a long chain is hard to trace and debug. Object identity is obscured, so code relying on reference equality or instanceof against concrete types can break. Configuring the right stack can also become complex.
Related Patterns
Decorator shares its recursive wrapping structure with Composite but adds behavior to one object rather than aggregating many. It differs from Adapter, which changes an interface, and from Proxy, which controls access without adding new responsibilities — Decorator's purpose is specifically to add behavior. Strategy is an alternative that changes behavior by swapping a plugged-in algorithm instead of wrapping.
Example
A text stream starts as a FileStream implementing Stream. Wrapping it in a BufferingStream, then a GzipStream, then an EncryptingStream yields a stream that buffers, compresses, and encrypts on write — each layer added by composition. Reading uses the same interface in reverse. Any subset and order of these features is available without writing a BufferedGzipEncryptedFileStream class for every combination.