Decorator
Description
Decorator wraps a subject in another object that exposes the same interface but inserts its own behavior around the wrapped call. Because the decorator honors the subject’s interface, decorators can be composed — wrap a decorator with another decorator with another, and each layer contributes its added behavior to the stack. The composition order is part of the semantics: logging-on-the-outside-of-caching is not the same shape as caching-on-the-outside-of-logging. The cross-domain image is gift wrap: the inner box is the subject; each layer of paper-and-ribbon is a decorator; the recipient still receives “a present,” but the experience of unwrapping is determined by the stack. In code, decorator and middleware are essentially the same primitive: the middleware stack of an HTTP framework, the decorators in Python’s@decorator syntax, the higher-order-functions composition in functional code — all are decorator-shaped.
Triggers
User-initiated: User wants to add cross-cutting behavior (logging, retry, caching, auth, validation) without modifying the underlying object. Vocabulary cues: “wrap this,” “add behavior around,” “middleware,” “cross-cutting,” “without modifying.” Agent-initiated: Agent notices behavior that would otherwise have to be added inline at many call sites (logging, retry, instrumentation) but could be lifted into a wrapper that composes uniformly. Candidate inference: “extract this as a decorator on the subject’s interface; stack with existing wrappers.” Situation-shape signals: Repeated pre/post patterns around a single operation. Configurable behavior that should be optional and composable. Stackable behavior that varies across use sites (some calls want retry; some want logging; some want both).Exclusions
- The added behavior is intrinsic, not orthogonal — if the “decoration” fundamentally changes what the subject is or means, decorator is the wrong frame; it’s a subclass or a different concept altogether.
- Stacking order is implicit or undefined — if the order of wrappers doesn’t matter (or if the framework doesn’t expose it), most of decorator’s value is lost; the composition has to be intentional.
- Excessive layering — five-deep middleware stacks become opaque; the convenience of “just add another decorator” can hide structural problems (e.g., the same concern handled at three different layers).
- Performance critical paths — every decorator adds one indirection; in microbenchmark-shaped contexts the layering cost dominates.
Structure
Relationships
- stack-layer — decorators are the canonical “stackable layers with a uniform interface” instance; reading the decorator stack as a stack-layer-shape clarifies why ordering matters.
- uniformity-dividend — decorators compose precisely because they all honor the same interface; the uniformity is what unlocks the composition.
- proxy — sibling pattern; same wrap-the-subject shell, different intent (access control vs. behavior augmentation).
- adapter — anti-sibling; adapter wraps to change the interface; decorator wraps to preserve it.
- surface — the decorator stack collectively forms a new surface for the subject; deeply stacked decorators can make the effective surface diverge significantly from the original.
Examples
Python `@decorator` syntax · computer-science
Python `@decorator` syntax · computer-science
@cache, @retry, @require_auth stack on a function.Gift wrap / Russian nesting dolls · visual-arts
Gift wrap / Russian nesting dolls · visual-arts
Aspect-oriented programming (AOP) · computer-science
Aspect-oriented programming (AOP) · computer-science
Functional composition · computer-science
Functional composition · computer-science
f ∘ g ∘ h is decorator viewed from FP-shaped seats.Gamma, Helm, Johnson, Vlissides (1994), Design Patterns: Elements of Reusable Object-Oriented Software (Gang of Four). Structural pattern, ch. 4. · computer-science
Gamma, Helm, Johnson, Vlissides (1994), Design Patterns: Elements of Reusable Object-Oriented Software (Gang of Four). Structural pattern, ch. 4. · computer-science
LoggedStream, AuthenticatedStream, LoggedAuthenticatedStream, CompressedStream, LoggedCompressedStream, etc. Decorator collapses the combinatorics to runtime composition: Logged(Authenticated(Compressed(Stream))). The wider “wrap to extend behavior without subclassing” shape recurs across HTTP middleware stacks (an HTTP request passes through logging, auth, compression, and rate-limiting middlewares in turn — each preserves the request-handler interface), Python decorators (syntactic sugar around higher-order functions), Java/C# stream wrappers, gift wrap (a wrapped gift still hands like a gift, just with an extra step before access), functional composition (f ∘ g presents the same callable signature as f or g alone), and Russian nesting dolls (each doll has the doll-shaped interface, the inner content remains accessible by unwrapping).Inference: The independence of these instances across software, packaging, mathematics, and folk craft is what makes Decorator a portable structural primitive — and the subclass-explosion-avoidance argument is what makes it specifically a good engineering move rather than just a curiosity.GoF Decorator · computer-science
GoF Decorator · computer-science
Java/C# stream wrappers · computer-science
Java/C# stream wrappers · computer-science
BufferedInputStream(GZIPInputStream(FileInputStream(file))) — each wrapper adds behavior.Middleware-stack architectures in web frameworks (Rack 2007, Express, Connect). · computer-science
Middleware-stack architectures in web frameworks (Rack 2007, Express, Connect). · computer-science