skip to content

How does the Proxy pattern differ from the Decorator pattern, given that both wrap an object behind the same interface?

level: middleimportance: must knowfreq 68%

answer

  1. Same shape, different intent
  2. Proxy: controls access; Decorator: adds responsibility
  3. Proxy may not call through
  4. Proxy often creates/owns subject; decorator gets it injected
  5. Decorators stack by design

basics

~20 s

Their structures look alike, but the intent differs. Proxy controls access to the object — whether, when, and from where you reach it — and usually creates or owns it. Decorator adds extra behavior to a request that always reaches the wrapped object, and is composed from outside.

solid answer

~60 s

Structurally both wrap a target behind a shared interface, so a UML diagram alone can't tell them apart; the distinction is intent and lifecycle. **Proxy** controls access: it may refuse the call (protection), delay creating the subject (virtual), send it across a process boundary (remote), or serve it from a cache. It typically knows the concrete subject type and controls its lifecycle — a virtual proxy constructs it, a remote proxy locates it. There is usually one proxy per subject and it isn't meant to be stacked arbitrarily. **Decorator** adds responsibilities to an object that already exists: the call is expected to reach the delegate, and decorators are designed to be stacked and composed by the client at runtime (compression over buffering over a raw stream). The wrapped object is injected, not created. Practical test: if the wrapper can plausibly *not* call through, or exists so the client needn't know where/when the subject is, it's a Proxy; if it enriches the result of a call that always happens, it's a Decorator.

go deeper

for a junior

Say the structures look the same and give the one-line intent split: Proxy controls access, Decorator adds behavior. One example each is enough.

for a middle

Add the practical discriminators: can the call be skipped, and who creates the wrapped object. Give a lazy/protection proxy and a stacked stream decorator as examples.

for a senior

Discuss grey areas (caching, retry, logging), the LSP tension when a protection proxy throws, and how the naming changes what reviewers and tests expect.

for a principal

Talk about it as a system-level decision: interception seams, when a proxy chain should become an explicit interceptor/middleware pipeline, and how proxy semantics leak into contracts and observability.

## Why the confusion exists Both patterns produce the same picture: ``` Client -> Subject (interface) ^ ^ | | RealSubject Wrapper --has-a--> Subject ``` The wrapper implements the interface it wraps, so from a class diagram Proxy, Decorator, and even the pass-through part of Adapter/Chain-of-Responsibility can look identical. The Gang of Four classify patterns by **intent**, not shape, so you must reason about *why* the indirection exists. ## Definitions, restated precisely - **Proxy** — "Provide a surrogate or placeholder for another object to **control access** to it." The proxy stands *in place of* the subject. It may decide the subject is never touched at all (denied by a protection proxy, served from cache by a caching proxy, not yet created by a virtual proxy). - **Decorator** — "Attach **additional responsibilities** to an object dynamically." The decorator *augments* the subject. The underlying call is the point; the decorator adds something before, after, or around it. ## The discriminators | Dimension | Proxy | Decorator | |---|---|---| | **Intent** | Control access — whether/when/where | Add behavior — what else happens | | **Does the call reach the target?** | Possibly not (denied, cached, not yet created, unreachable) | Yes, essentially always | | **Who supplies the target?** | The proxy itself often creates or locates it (constructs lazily, resolves a remote endpoint, looks it up in a registry) | The client injects an already-existing instance | | **Lifecycle ownership** | Frequently owns it | Never owns it | | **Composition** | Usually one, fixed at design time | Explicitly stackable; order is meaningful | | **Client awareness** | Client typically has no idea; obtains the proxy from a factory/container | Client deliberately assembles the chain | | **Interface knowledge** | Often knows the *concrete* subject type (to instantiate it) | Knows only the *interface* | ## Worked examples **Proxy:** - `LazyImage` that reads the file on first `render()` — the file may never be read. - `SecureAccountService` that throws unless the caller has role `TELLER` — the call may never reach the account service. - A gRPC client stub — the "object" is on another machine. - An ORM lazy collection — iterating triggers the SELECT; not iterating means no SELECT. **Decorator:** - `BufferedStream(GZipStream(FileStream(...)))` — every write goes all the way down; each layer transforms it. - `PricedWithTax(PricedWithDiscount(basePrice))` — each layer adjusts the number the inner one returned. - A UI `ScrollableWindow(BorderedWindow(window))` — both add chrome around a window that certainly still draws. ## Grey areas (and how to resolve them) - **Caching wrapper** — Proxy. It exists to avoid reaching the subject. (Some authors call it a decorator because it "adds caching"; the GoF-consistent read is Proxy, since access is what's controlled.) - **Logging wrapper** — Decorator by intent (adds an incidental responsibility, always calls through). An *auditing* wrapper that can also block is a protection proxy. - **Retry wrapper** — Proxy-ish (a smart reference around a remote subject) since it governs how/whether the subject is reached; it usually pairs with a remote proxy. - **Rate limiter / circuit breaker** — Proxy: their whole job is to sometimes *not* forward. ## Why the distinction matters practically 1. **Reviewer vocabulary** — naming the class `OrderServiceProxy` vs `LoggingOrderService` tells the next reader whether skipping the delegate is legal. 2. **Testing** — a decorator's tests assert the delegate was called and the result transformed; a proxy's tests must include the *not forwarded* paths (denied, cached, lazy). 3. **Design pressure** — decorators are expected to compose in any order; proxies aren't, so stacking five proxies usually signals a missing abstraction (or a job for an interceptor pipeline). 4. **Substitutability** — both must satisfy the Liskov Substitution Principle for the shared interface, but a protection proxy that throws where the subject wouldn't is a real LSP tension you must document in the contract (e.g. the interface declares an authorization failure). ## Nearby cousins, for completeness - **Adapter** — changes the interface (Proxy keeps it identical). - **Facade** — one simplified entry point over a *set* of objects (Proxy mirrors one object). - **Chain of Responsibility** — a wrapper chain where a link may handle and stop, but links needn't share the handled object's interface and the chain, not access control, is the point.

  • Where does Adapter fit next to Proxy and Decorator?
    Adapter changes the interface so an incompatible client and service can talk; Proxy and Decorator both preserve the interface. So: Adapter = different interface, Proxy = same interface + access control, Decorator = same interface + extra behavior.
  • Is a caching wrapper a proxy or a decorator?
    Proxy, on the GoF reading: its purpose is to avoid reaching the real subject on a hit, so it controls access. A wrapper that always calls through and merely records timings is a decorator.
  • Both patterns wrap behind one interface — does that mean I can refactor one into the other freely?
    Mechanically yes, semantically no. Turning a decorator into something that can skip the delegate changes the contract clients rely on; turning a protection proxy into an always-forwarding decorator removes a security control. The rename should follow a deliberate change of intent.

A bodyguard versus a coat. The bodyguard (proxy) can stop you reaching the person entirely, and decides when they're available. The coat (decorator) adds warmth to the person who is definitely still there — and you can put a scarf over it, and a hat over that.

saying these in an interview costs you the question

  • Claiming the two patterns have different class diagrams — they don't; only intent separates them
  • Saying a proxy always forwards the call (a protection, caching, or circuit-breaking proxy often doesn't)
  • Saying Proxy changes the interface — that's Adapter
  • Saying Decorator controls access or manages the target's lifecycle
  • Assuming proxies are meant to be stacked like decorators

context