Adapter, Decorator, Proxy, and Facade all wrap one object (or several) behind another object. What distinguishes them, and how do you pick between them when you are about to introduce a wrapper?
answer
- Adapter = different interface, fixes mismatch
- Decorator = same interface, adds behaviour, stacks
- Proxy = same interface, controls access, usually one
- Facade = new simpler interface over a subsystem
- Decorator order is semantics, not style
basics
~20 sBy what the wrapper does to the interface and the behaviour. Adapter changes the interface to one the client expects. Decorator keeps the same interface and adds behaviour, stackably. Proxy keeps the same interface and controls access (lazy, remote, caching, permission). Facade offers a simpler interface over a whole subsystem.
solid answer
~50 sAsk two questions: does the wrapper expose the same interface as what it wraps, and why does it exist? Adapter — different interface, existing incompatibility; it translates so an existing client can use an existing class, and is usually forced on you by a third-party or legacy API. Decorator — same interface, added responsibility (logging, retry, caching of results, encryption); designed to nest, so order matters and many can stack. Proxy — same interface, but it governs *whether, when, and where* the call reaches the subject: lazy creation, remote invocation, access control, reference counting, memoisation. Structurally Proxy and Decorator are twins; intent differs, and Proxy typically owns/creates its subject while a Decorator is handed one. Facade — new, narrower interface over many collaborating classes to reduce coupling and cognitive load; it does not aim to be substitutable for anything. Selection failure modes: piling Decorators until the runtime composition is unreadable, and Facades that grow into a god object.
go deeper
State the four intents in one line each and note that the deciding questions are 'same interface or different?' and 'why does the wrapper exist?'.
Work a concrete example through the two questions, and mention Decorator stacking with order-dependence and Proxy variants (virtual, remote, protection, caching).
Discuss framework-generated proxies and their surprises, the class-explosion argument for Decorator over subclassing, ports-and-adapters, and the runtime cost of deep wrapper stacks.
Talk about where wrappers belong architecturally — cross-cutting concerns as decorators versus middleware versus infrastructure — and about governing facade growth so boundaries stay use-case shaped rather than becoming god objects.
## The shared shape All four are **structural patterns** built from object composition: an outer object holds one or more inner objects and forwards calls. Because the drawing is the same, you must select on intent. Two questions resolve almost every case: **Q1 — Is the wrapper's interface the same as the wrapped thing's?** - Same → Decorator or Proxy (both are *transparent*: clients cannot tell). - Different, and it matches what a client already demands → Adapter. - Different, and it is a new simplified entry point over many classes → Facade. **Q2 — Why does it exist?** - To fix an incompatibility → Adapter. - To add behaviour → Decorator. - To control access to the real thing → Proxy. - To hide a subsystem → Facade. ## Adapter **Intent:** convert the interface of a class into another interface clients expect, letting classes work together that could not otherwise. - Driven by a **mismatch you did not choose** — a vendor SDK, a legacy module, two libraries with different `Money` types. - Typically **one adapter per incompatible pair**; adapters do not stack. - Also the natural implementation of a **ports-and-adapters** boundary: your domain declares the port (interface it wants), and an adapter satisfies it with a concrete technology. - Trap: an "adapter" that also adds retries and metrics is no longer only an Adapter; split it, or name it honestly. ## Decorator **Intent:** attach additional responsibilities to an object dynamically, as a flexible alternative to subclassing. - **Same interface in and out**, which is what makes stacking possible: `Metrics(Retry(Caching(Real())))`. - Chosen when the extra behaviour is **orthogonal** to the core and you want to mix combinations without a class explosion (subclassing every combination is N×M classes). - **Order is semantics**, not style: retry inside caching caches failures differently than caching inside retry; metrics outermost measures the retries, innermost measures single attempts. This is the pattern's main hazard — the effective behaviour lives in wiring code, not in any one class. - Debugging cost: deep stacks, and "which layer swallowed the exception?" becomes a real question. ## Proxy **Intent:** provide a surrogate or placeholder for another object to control access to it. Common variants: **virtual** (create the expensive subject on first use), **remote** (the subject lives in another process; the proxy marshals the call), **protection** (check permissions before forwarding), **caching/memoising**, **smart reference** (counting, locking, lifecycle). - Same interface as the subject, like Decorator. Practical discriminators: a Proxy usually **owns or locates** its subject rather than receiving it; there is normally **one** proxy, not a stack; and the interesting logic is about *whether/when/where* the call happens rather than *what extra work* accompanies it. - Frameworks generate proxies for you (transactions, security, lazy-loaded ORM relations). Knowing this explains classic surprises like a self-invocation bypassing the proxy, or a lazy relation failing outside its session. ## Facade **Intent:** provide a unified, higher-level interface to a set of interfaces in a subsystem, making it easier to use. - **New interface, not a substitutable one.** Clients depend on the Facade instead of on a dozen classes, which cuts coupling and shrinks the surface a newcomer must learn. - It does not forbid direct access to the subsystem; advanced callers may bypass it. - Failure mode: a Facade that accumulates every operation in the system becomes a god object and a change bottleneck. Keep facades use-case-shaped and split them by client need. ## Selection walkthrough > "I need to add caching to a payment gateway client." - Interface stays the same for callers → Decorator or Proxy. - Caching is about avoiding a call to the real subject → that is *controlling access*, i.e. a caching **Proxy**; many teams would still call it a Decorator, and the code is nearly identical. When intent is genuinely ambiguous, choose the name that communicates best to your readers and move on — the design is the same; only the label is contested. > "The vendor's SDK exposes `chargeCard(Map)` and my domain wants `PaymentPort.charge(Payment)`." - Different interfaces, forced mismatch → **Adapter**. > "Onboarding a customer means calling six services in order with compensation on failure." - New coarse operation over a subsystem → **Facade** (and the orchestration inside it may itself use other patterns). ## Cost of any wrapper Every wrapper adds a hop: another type to navigate, another frame in the stack, another place a null/exception can be introduced or hidden, and a risk that equality, identity, or interface-default methods behave unexpectedly through the wrapper. Wrap when the added seam buys substitutability, composability, or isolation you will actually use.
- Proxy and Decorator have the same structure. Give a rule of thumb an interviewer would accept for telling them apart in real code.Look at ownership and multiplicity. A Decorator is handed the object it wraps and is designed to stack, adding work around a call that still happens. A Proxy usually creates, locates, or gates its subject, exists singly, and its logic decides whether the call reaches the subject at all — lazy creation, remoting, permission checks, cache hits.
- What goes wrong when a Decorator stack gets deep?The effective behaviour is defined by wiring order that lives nowhere in the classes themselves, so reading any single class tells you little. Stack traces balloon, exception handling and short-circuiting in one layer silently change others, and metrics can measure the wrong scope. Mitigations: keep the stack shallow, build it in one documented factory, and test the composed object, not just the layers.
- When is a Facade the wrong answer for reducing coupling?When the underlying subsystem is the real problem. A Facade hides complexity but does not remove it, and a facade over an incoherent subsystem tends to grow into a god object with a method per caller. If clients keep needing to reach behind it, the boundary is wrong and needs redesigning, not covering.
Adapter is a plug converter; Decorator is a surge protector you can chain; Proxy is the receptionist deciding whether, when, and to whom your call goes through; Facade is the single 'concierge' desk instead of learning every department's phone number.
saying these in an interview costs you the question
- Saying Proxy and Decorator differ structurally — they do not; the difference is intent, ownership, and multiplicity.
- Calling every wrapper an 'Adapter' regardless of whether an interface mismatch exists.
- Believing Decorator order does not matter.
- Treating a Facade as something clients must be forced through, or letting it accumulate every operation until it is a god object.
- Adding a wrapper layer with no substitutability or composition benefit — pure indirection tax.