Adapter, Mediator, Controller, Facade, Repository and Proxy are all forms of indirection. What distinguishes them, and how do you pick the right one?
answer
- Adapter = different interface; Proxy = same interface
- Facade = simplify many; Adapter = translate one
- Mediator = N peers, hub owns the rules
- Controller = first receiver of a system event
- Repository/Gateway = hide infrastructure
basics
~20 sAll put an object in the middle, but for different jobs: Adapter translates between two vocabularies, Mediator coordinates many peers, Controller receives input events, Facade simplifies a big subsystem, Repository hides storage, Proxy stands in for the real thing.
solid answer
~50 sThey share the mechanism (an intermediate object) and differ in the *responsibility* assigned to it: - **Adapter** — translation. Two parties with incompatible vocabularies/protocols; the adapter converts each way. Points *outward* at one foreign system. - **Facade** — simplification. One coarse entry point over a complex subsystem; hides many collaborators, not one incompatible one. - **Mediator** — coordination. N peers that would otherwise form an N×N mesh talk only to a hub, which owns the interaction rules. - **Controller** — first receiver of a system input event (HTTP request, UI action); keeps the UI/transport out of the domain and delegates all real work. - **Repository/Gateway** — hides a storage or remote resource behind a collection-like or domain-shaped API. - **Proxy** — same interface as the real object, added to control access: lazy loading, remoting, caching, permission checks. Pick by asking what the middle object is *for*: translate, simplify, coordinate, receive, persist, or control access. If you cannot answer, you don't need it.
go deeper
Give the one-line job of each: adapter translates, facade simplifies, controller receives requests, repository hides the database.
Use the interface test to separate Adapter (different interface) from Proxy/Decorator (same interface), and explain the N×N-to-N argument for Mediator.
Talk about choosing by responsibility rather than by habit, and about detecting pass-through layers and god mediators in an existing codebase.
Discuss the cumulative cost across a system — how many hops a request crosses, who owns each contract, and whether the layer count is justified by real variation and team boundaries rather than convention.
## One mechanism, many responsibilities The GRASP **Indirection** principle says: to keep two components decoupled, give an intermediate object the responsibility of mediating between them. That is deliberately abstract. Most of the classic Gang-of-Four structural/behavioural patterns are *specializations* of it, differing in what the middle object is responsible for. ### Adapter — translate **Situation:** you have a client that speaks contract `X` and a supplier that speaks contract `Y`. You cannot (or should not) change either. **Middle object's job:** convert calls and data between the two vocabularies, including error models (`HTTP 429` → `RateLimited`), units (cents ↔ decimal), and naming. **Signature test:** *does the middle object exist because two APIs are shaped differently?* If yes → Adapter. Typical: a `PaymentGateway` implementation wrapping a vendor SDK; a legacy driver wrapped in a modern port. ### Facade — simplify **Situation:** a subsystem has many classes with a fiddly call sequence. Clients only need three or four coarse operations. **Middle object's job:** provide a small, convenient, high-level API over the subsystem; still allows direct access to internals if needed. **Signature test:** *does the middle object exist because the far side is complicated, not because it is incompatible?* Adapter changes the *shape*; Facade changes the *granularity*. ### Mediator — coordinate **Situation:** several peer objects need to react to each other. Wiring them pairwise gives O(N²) references and makes each peer know all the others. **Middle object's job:** own the interaction rules. Peers notify the mediator; the mediator decides who else must change. Typical: a dialog where enabling the Save button depends on three fields; air-traffic control between aircraft; an event bus. **Signature test:** *are there more than two participants, and do the rules of their interaction live nowhere?* If yes → Mediator. Caveat: a mediator can silently become a god object holding all business logic. ### Controller — receive the input event **Situation:** an external actor triggers a system operation (an HTTP request, a CLI command, a button press). **Middle object's job:** be the *first* object beyond the UI/transport layer to receive the event, then delegate. It must **not** contain business logic — GRASP's Controller principle explicitly warns about the "bloated controller". **Signature test:** *is the far side a delivery mechanism (UI, HTTP, queue) rather than another domain object?* If yes → Controller. This is the indirection that keeps the domain reusable across delivery mechanisms. ### Repository / Gateway — hide the resource **Situation:** the domain needs to load and store data, but must not know about SQL, ORMs, HTTP, or file layouts. **Middle object's job:** present a domain-shaped, often collection-like API (`findActiveCustomers()`, `save(order)`) and hide the technology entirely. A *Gateway* is the same idea for a non-storage external resource (a mail server, a fraud service). **Signature test:** *is the far side infrastructure/persistence?* If yes → Repository/Gateway. Also the primary seam for testing without a database. ### Proxy — same interface, controlled access **Situation:** you want to intercept use of an object without the client knowing. **Middle object's job:** implement *the same* interface as the real subject and add a policy: lazy instantiation (virtual proxy), network transparency (remote proxy), caching, authorization (protection proxy), reference counting. **Signature test:** *does the middle object have the identical interface to the thing it stands in for?* If yes → Proxy. Adapter deliberately has a *different* interface from its adaptee; Proxy deliberately has the *same* one. That is the crispest way to tell them apart in an interview. ### Decorator — a near neighbour Same interface as the wrapped object (like Proxy), but the purpose is to *add behaviour* compositionally (buffering, compression, retry), often stacking several. Proxy controls *access*; Decorator adds *responsibility*. ## Comparison table | Form | Middle object's job | Interface vs far side | Party count | |---|---|---|---| | Adapter | Translate vocabularies | Different by design | 1 client ↔ 1 supplier | | Facade | Simplify / coarsen | Smaller, higher-level | 1 client ↔ many internals | | Mediator | Own interaction rules | Its own | N peers | | Controller | Receive system events | Its own (use-case shaped) | UI/transport ↔ domain | | Repository/Gateway | Hide infrastructure | Domain-shaped | domain ↔ storage/remote | | Proxy | Control access | Identical | 1:1 with real subject | | Decorator | Add behaviour | Identical | stackable 1:1 | ## How to choose, in one procedure 1. **Name the two (or N) parties.** If you can't, you have no coupling to break. 2. **Name why they must not touch.** Foreign vocabulary → Adapter. Complexity → Facade. Combinatorial peer wiring → Mediator. Delivery mechanism → Controller. Infrastructure → Repository/Gateway. Access policy → Proxy. 3. **Decide whether the middle needs an interface.** Only if you need substitutability (test double, second implementation, runtime selection). Otherwise a concrete class is enough indirection. 4. **Check the contract is yours, not theirs.** If the mediator's method names and types are the far side's, you built a pass-through, not a boundary. ## Common pitfalls - **Pass-through layers**: `Controller → Service → Manager → Repository` where each layer only forwards. Every hop should *add* something (translation, coordination, transaction, authorization) or be deleted. - **The god mediator**: coordination logic quietly becomes all the business logic. - **The leaky repository**: returning ORM entities or exposing query-builder objects reintroduces exactly the coupling it was meant to remove. - **Adapter that adapts nothing**: an interface identical to the vendor's SDK, so swapping vendors still touches every caller.
- What is the single crispest difference between Adapter and Proxy?The interface. A Proxy implements exactly the same interface as the object it stands in for, so the client cannot tell the difference. An Adapter deliberately exposes a *different* interface from its adaptee — its whole purpose is that the two shapes don't match.
- When does a Mediator become a liability?When 'coordination' quietly absorbs domain rules and it turns into a god object: every peer change means editing the mediator, the mediator has no cohesive theme, and its tests need the whole world. The fix is to push rules back into the peers and keep only routing/orchestration in the middle.
- How do you spot a pass-through layer that should be deleted?Each method forwards one-to-one with no translation, no policy, no transaction, no aggregation, and identical parameter types. If removing the layer would change nothing except import statements, it is buying you traceability cost and no decoupling.
Different roles in one building: a translator (adapter), a receptionist who takes your request and routes it (controller), a switchboard connecting everyone (mediator), an information desk that summarizes a huge department (facade), and a security guard who looks exactly like the door you wanted but decides whether you pass (proxy).
saying these in an interview costs you the question
- Calling every wrapper an 'adapter' — if the interface is identical to the wrapped object, it is a proxy or decorator, not an adapter.
- Believing a Facade and an Adapter are the same because both wrap something — one coarsens a complex subsystem, the other reconciles incompatible shapes.
- Putting business rules in a Controller — GRASP explicitly names the 'bloated controller' as the failure mode; the controller should delegate.
- Adding a mediator for exactly two collaborators — with N=2 there is no mesh to collapse; you have added a hop for nothing.
- A repository that returns ORM entities or exposes the query builder — the infrastructure coupling it was meant to hide leaks straight through.