Adapter, Decorator, Proxy and Facade are all called "wrappers". Given a wrapper class you've just found in a codebase, how do you tell which of the four it is?
answer
- Different interface → Adapter
- Same interface + behavior → Decorator
- Same interface + access control → Proxy
- New simple interface over subsystem → Facade
- Intent decides, not the diagram
basics
~10 sCompare the wrapper's interface to the wrapped thing's. Different interface, same behavior = Adapter. Same interface, extra behavior = Decorator. Same interface, controlled access = Proxy. New simpler interface over many objects = Facade.
solid answer
~50 sAsk two questions: does the wrapper expose the *same* interface as what it wraps, and does it *change behavior*? **Adapter** — different interface, behavior unchanged; it exists because a client expects another shape, and it wraps one collaborator. **Decorator** — same interface, behavior augmented (logging, caching, retry, encryption), and crucially it's designed to nest recursively, so N decorators stack in any order. **Proxy** — same interface, behavior nominally unchanged but access is *controlled*: lazy creation (virtual), remote invocation, permission checks, reference counting; the client should not notice it's there. **Facade** — a brand-new, simplified interface over a whole subsystem of several objects; clients may still bypass it and use the subsystem directly. Two clarifiers: intent, not structure, decides — the same class can be an adapter in one relationship and a proxy in another — and Decorator/Proxy preserve substitutability with the wrapped type, while Adapter deliberately breaks it.
code
pseudocode · 20 lines// ADAPTER — interface changes, behavior does not
class CsvAsRecordSource implements RecordSource {
constructor(csv: CsvLibrary) {}
next() = toRecord(csv.readRow()) // translation only
}
// DECORATOR — same interface, behavior added, nests
class CachingSource implements RecordSource {
constructor(inner: RecordSource) {} // <-- same type in, same type out
next() = cache.getOrPut(key) { inner.next() }
}
// usage: CachingSource(RetryingSource(CsvAsRecordSource(lib)))
// PROXY — same interface, access controlled / subject created lazily
class LazySource implements RecordSource {
next() { if (real == null) real = expensiveOpen(); return real.next() }
}
// FACADE — new, simpler interface over several subsystem types
class ImportFacade { run(file) { parser…; validator…; repo…; auditor… } }go deeper
Give the four one-liners: different interface = Adapter, same interface + extra behavior = Decorator, same interface + access control = Proxy, simplified interface over a subsystem = Facade.
Walk the decision procedure, note Decorator's recursive same-type constructor as its structural tell, and give one concrete example of each.
Discuss intent over structure, Proxy flavors (virtual/remote/protection/smart reference), and how one class can play two roles legitimately.
Talk about keeping translation separate from cross-cutting policy (resilience, caching, auth), where each wrapper belongs in the module graph, and how transparent proxying by frameworks affects debuggability and observability.
## Why the confusion exists All four are "an object holding another object and forwarding calls". Their UML overlaps almost completely. GoF distinguishes patterns by **intent**, so identifying one means reading the *purpose*, not counting the arrows. ## The decision procedure Start at the wrapper and answer in order: **Q1 — How many things does it wrap, and does it introduce a *new, simpler* vocabulary over a subsystem?** - Wraps a *set* of collaborating classes and offers a small entry point for common workflows → **Facade** (e.g. `OrderPlacement.place(cart)` orchestrating inventory, pricing, payment, shipping). The subsystem stays public; the facade is a convenience, not a wall. **Q2 — Is the wrapper's interface the *same* as the wrapped object's?** - **No, it's different** → **Adapter**. The client demanded interface A, the collaborator offers B. Behavior is meant to be equivalent; only the shape changes. Substitutability with the adaptee is deliberately broken — that's the point. - **Yes, identical** → continue. **Q3 — Does it add behavior the caller *wants* and might want several of?** - Yes, and instances nest (`Retry(Cache(Timing(realService)))`) → **Decorator**. Hallmarks: the wrapper takes the same interface as its constructor argument (so decorators can wrap decorators), the composition is chosen by the client, and each layer is optional. **Q4 — Does it instead control *whether/when/where* the call reaches the subject?** - Yes → **Proxy**. Flavors: *virtual* (create the expensive subject on first use), *remote* (marshal the call over a network), *protection* (authorize the caller), *smart reference* (ref-counting, locking, load-on-access), *caching*. The client is typically unaware — the proxy is imposed by infrastructure, whereas decorators are chosen by the caller. ## Side-by-side | | Interface vs. wrapped | Behavior | Wraps | Chosen by | Nests by design | |---|---|---|---|---|---| | **Adapter** | Different | Same (translated) | One (sometimes a few) | Integrator | No | | **Decorator** | Same | Added | One, same-type | Caller/composition root | Yes | | **Proxy** | Same | Same, access controlled | One, same-type | Infrastructure | Rarely | | **Facade** | New & simpler | Orchestrated | Many | Library author | No | ## Sharpening Decorator vs. Proxy Both keep the interface. The practical separators: - **Who supplies the subject?** A decorator is *given* its subject (`new Cached(realRepo)`); a proxy often *creates/finds* it (lazily, remotely, via a registry). - **Is the wrapping visible in the code that uses it?** Decorator composition is explicit and intentional; proxying is frequently transparent (generated at run time, injected by a container/framework). - **Is stacking meaningful?** Decorators are meant to stack in varying orders; a proxy is usually singular. ## Sharpening Adapter vs. Facade Both present a different interface. Separators: - Facade's interface is **newly invented for convenience**; Adapter's is **pre-existing and imposed by the client**. - Facade covers a **subsystem** (many types); Adapter usually covers **one** collaborator. - Facade does not aim at *substitutability* with anything; Adapter exists specifically so an object can be dropped into a slot typed by the Target. - A facade may legitimately encode workflow/policy; an adapter should not. ## Edge cases you should be ready for - **A class can play two roles.** A `RemoteInventoryClient` that speaks your `Inventory` port over HTTP is an Adapter (interfaces differ) *and* a Remote Proxy (subject lives elsewhere). Name it for the dominant intent and say so. - **Adapter that adds retry** — it has quietly become adapter + decorator; either split it or accept the mixed role knowingly. Mixing makes the translation logic harder to test. - **Facade that grows god-like** — if it accumulates business rules it is no longer a facade but an application service; that may be fine, but call it what it is. - **A wrapper with a *narrowed* interface** (fewer methods, same names) is closest to Facade or a protection Proxy, not Adapter.
- You find a class that both converts a vendor SDK's interface to yours and adds retry with backoff. What is it, and is that a problem?It's an Adapter that has absorbed a Decorator's responsibility. Not fatal, but the translation logic and the resilience policy have different reasons to change and different test needs. Prefer a thin adapter plus a separate retry decorator over the target interface — then retry is reusable across adaptees and testable without the vendor.
- Both Decorator and Proxy keep the interface. Name two practical tells that separate them in a real codebase.(1) Constructor: a decorator receives the subject as a parameter and is composed explicitly at a wiring site; a proxy typically creates, resolves or connects to its subject itself. (2) Multiplicity: decorators are designed to stack in multiple layers and orders; proxies are usually singular and often invisible/generated by a framework.
- Is a logging facade (one API routing to whichever logging backend is present) a Facade or an Adapter?Despite the name, the per-backend classes are Adapters — they translate one fixed API onto each backend's differing API. The overall arrangement is closer to a port with pluggable adapters (bridge-like) than to a subsystem-simplifying Facade.
Four people standing between you and a specialist: the interpreter (Adapter) conveys the same message in a language you speak; the assistant (Decorator) passes your message on but also logs it and follows up; the receptionist (Proxy) decides whether your call gets through at all and may just take a message; the concierge (Facade) offers one simple 'arrange my whole trip' request over a dozen separate services.
saying these in an interview costs you the question
- Calling anything that wraps another object an Adapter.
- Saying Decorator changes the interface — it must preserve it, or the recursive stacking that defines the pattern is impossible.
- Insisting Proxy and Decorator are the same pattern because the diagrams match — intent (access control vs. added behavior) and how the subject is supplied differ.
- Describing Facade as 'an adapter for many classes' — a facade invents a convenience interface, an adapter conforms to a required one.
- Deciding by class-diagram shape instead of intent.