Skip to main content
computer-science visual-arts

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

Internal structure of decorator: a table of its component slots and the concepts that fill them.

Relationships

Relationship neighborhood of decorator: a graph of the concepts it connects to and the concepts it is a part of.
  • 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

@cache, @retry, @require_auth stack on a function.

Gift wrap / Russian nesting dolls · visual-arts

physical-world analogues; layered containment where each layer contributes to the experience.
cross-cutting concerns (logging, transactions, security) implemented as decorator-like advice around method calls.
f ∘ g ∘ h is decorator viewed from FP-shaped seats.
GoF Decorator is the canonical OOP articulation: a Decorator implements the Component interface, holds a Component reference, and forwards calls — adding behavior before or after the forward. The structural property that makes it valuable is that decorators stack — wrapping a decorator in another decorator yields something still presenting the Component interface, so clients are unaware of the wrapping depth.Decorator’s value is behavioral composition without subclass-explosion. The naive alternative — one subclass per combination of optional behaviors — multiplies combinatorially: 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.
the OOP coinage; example was scrollable / bordered window decorators.
BufferedInputStream(GZIPInputStream(FileInputStream(file))) — each wrapper adds behavior.
Web framework middleware stacks (Rack for Ruby, introduced 2007; Connect and Express for Node.js; ASP.NET’s pipeline; Python’s WSGI middleware) all implement Decorator as a runtime composition pattern: each middleware presents the same handler interface (take a request, return a response, or pass to the next handler), and a request flows through the stack in order. A logging middleware records, then delegates; an auth middleware checks credentials, then delegates; a compression middleware delegates first, then compresses the response on the way back out.The arrangement is functionally identical to GoF Decorator at runtime, but the pattern came into web frameworks largely independently from the OOP literature — its more direct ancestor is Unix pipeline composition and the higher-order-function tradition. That convergent rediscovery is part of why Decorator is a strong structural primitive: developers in the web-frameworks lineage arrived at the same wrap-to-extend shape without invoking GoF as the source, because the shape solves the problem of composing optional cross-cutting concerns over a fixed request/response interface.