Proxy
Stand in for another object behind an identical interface so you can control access to it - lazy loading, caching, permissions, logging - without touching it or its clients.
The problem
Why control access to an object at all? Start with the obvious case: a class that eats enormous resources - a database connection, a media decoder, a model loaded from disk. You need it occasionally, not constantly.
Lazy initialization is the fix, but where do you put it? Inside every client, duplicated and slightly wrong in at least one of them. The natural home is the service class itself, and that door is often locked: third-party code, a sealed class, or simply too many existing dependents to disturb.
The solution
Create a class with the same interface as the service, holding a reference to a real one. Hand the proxy to every client that used to receive the service. Requests arrive, the proxy does its extra work - checking a cache, verifying credentials, constructing the service if it does not exist yet - and then forwards the call.
Because the interfaces match, the proxy is substitutable everywhere the service was, so you get all this without editing the service or a single client.
- 1Configuration swaps in the proxy. This one line is the entire integration cost.
- 2The manager takes the interface type and cannot tell the difference. No client code changed.
- 3An ordinary call that used to go straight to the network.
- 4The proxy runs its own logic first. Everything the pattern buys you happens in this check.
- 5Cold cache, so the real request goes out over the wire exactly once.
- 6The proxy stores the result on its way through.
- 7A second later the UI asks again, as UIs do.
- 8Answered from memory. The service was never disturbed, and nobody upstream noticed.
Structure
The service interface is what makes substitution legal. The service does the useful work. The proxy holds a reference to it, usually creating and managing it for its entire lifetime, and does something before or after each forwarded call. The client works against the interface and never learns which it received.
Code
Caching bolted onto a third-party video library that never asked for it.
When to use it
- Lazy initialization (virtual proxy). A heavyweight object sits idle most of the time. Build it on first real use instead of at startup.
- Access control (protection proxy). Only clients with the right credentials get through to the service.
- Remote execution (remote proxy). The service lives on another machine and the proxy hides the network from the caller.
- Logging. Keep a record of every request before passing it along.
- Caching. Store results for recurring requests, keyed by the request parameters.
- Smart reference. Track who is still using the service and release it when the list empties.
Pitfalls
- Silent latency. Callers see a plain method call and assume plain method cost. A cache miss that crosses the Atlantic does not look any different at the call site.
- Stale caches. A caching proxy is a cache, with every invalidation problem caches have ever had. Decide the eviction rules before you ship it.
- Proxy sprawl. Logging, caching, retries and authorization as four nested proxies is a stack trace nobody enjoys reading.
- Leaking the real thing. A method that returns the underlying service hands out a route around every check the proxy exists to perform.
Don't confuse it with
- Decorator. Same skeleton, opposite emphasis. A decorator adds behavior and is stacked by the client deliberately, layer by layer. A proxy controls access and typically creates and owns its service object outright - the client often has no idea it exists.
- Adapter. An adapter changes the interface because the client speaks a different language. A proxy keeps the interface bit-for-bit identical, since substitutability is the point.
- Facade. Both buffer something heavy and can initialize it themselves. A facade presents a new, simpler interface over a whole subsystem and cannot be swapped in for it; a proxy mirrors one object exactly and can.
Check yourself
Why does a proxy implement the exact same interface as its service?