Home Design Patterns Proxy

Proxy

Structural Gang of Four

“Provide a surrogate or placeholder for another object to control access to it.”

Gamma, Helm, Johnson & Vlissides, p. 207

Purpose

The Proxy pattern can simulate another class until that class is actually needed. This allows developers to better manage resources. A basic example is how a modern browser loads a web page: it first loads just the structure of the page, then begins loading images and content that are currently visible on the screen. Eventually it has all of the page loaded, but the user did not have to wait for all of that to complete before they were able to view the page.

Controlling access is a broader job than deferring construction, and the book distinguishes several uses which are worth naming separately, because they have very little in common beyond their shape:

  • Virtual proxy. Defers creating an expensive object until it is really needed. The browser example above.
  • Remote proxy. Stands in locally for an object that lives in another process or on another machine, hiding the marshalling.
  • Protection proxy. Checks permissions before forwarding.
  • Smart reference. Does bookkeeping on access: reference counting, locking, caching, audit logging.

Design

The design of the proxy class is to have two classes that inherit from a primary class, the Subject. One of them is the proxy class that pretends to be the RealSubject until that subject is actually needed or ready. Once it is ready, the proxy forwards to the RealSubject when the Subject is requested.

UML class diagram: Proxy and RealSubject both implement Subject; Proxy holds a reference to RealSubject.
Because both implement Subject, the client's type does not change when a proxy is inserted. That transparency is the point, and it is also what makes proxies easy to miss when reading unfamiliar code.

Example

Autobot Robotics wanted to speed up the startup time of their application. They decided to move the bulk of the code into DLLs that could be loaded dynamically. Initially, just a basic framework of the program is loaded. As the user requests more sophisticated behavior, the software uses a proxy to load that code into memory as it is needed. This resulted in a dramatic reduction in startup time.

The measurable part is worth stating plainly, because it is the argument that sold the change internally: operators start the application at shift change, and several dozen seconds of staring at a splash screen, several times a day, across every station, is real production time. The proxy did not make the program faster. It made it start before it was finished loading, which is a different thing and the thing that actually mattered.

Comparisons

  • Decorator is nearly indistinguishable in UML. The difference is intent and ownership: a Decorator is handed a Component and adds behavior to it; a Proxy typically creates or locates its subject and controls access to it. If you removed the wrapper and the caller lost functionality, it was a Decorator; if it lost control, it was a Proxy.
  • Adapter changes the interface. A Proxy keeps it identical.
  • Facade simplifies a group of objects into a new interface. A Proxy mirrors one object's existing interface exactly.
  • Lazy Load is the virtual proxy's job described as a data-access concern rather than an object-structure one.

Why It Exists

My take

Proxy exists because "when does this work happen?" and "what does this code do?" are separate questions, and languages give you no way to change the first answer without editing the second. Deferring an expensive operation, moving it across a network, or checking a permission first are all decisions about policy, and none of them belong in the class doing the actual work. A proxy is where you put policy so the subject can stay ignorant of it.

The reason it is worth learning as a named pattern rather than an obvious trick is that transparency is deceptively hard to get right. A proxy that is truly indistinguishable from its subject has to handle the awkward cases too: what happens on equality comparison, on identity checks, on serialization, when an exception is thrown during the deferred load, when two threads trigger that load at once. Every one of those has bitten somebody, and the pattern's value is partly in telling you the list exists.

My honest view is that this is the pattern most likely to already be provided for you. ORM lazy loading, RPC stubs, virtual memory itself, copy-on-write filesystem snapshots: all proxies, and you implement none of them. Knowing the pattern is mostly so you can recognize what a framework is doing to you, and why a field access just made a database call.

Criticisms

The detection of when an object is needed can complicate the design. As with most design patterns, the proxy pattern should only be used when it is needed.

Its transparency is also a genuine liability. A method call that looks local may cross a network; a field access that looks free may hit a database. The code reads as though nothing is happening, and the performance characteristics say otherwise. The N+1 query problem is this pattern's fault more often than it is anyone else's.

Deferred work also relocates failure. An object constructed successfully may throw the first time you touch it, which means errors surface far from their cause and in call sites that had no reason to expect them.