Skip to main content

Proxy

Proxy stands in for another object behind the same interface to control access, enabling lazy loading, protection, caching, and remote calls transparently. Its extra indirection adds latency and complexity.

Type
Structural
When to Use
Control Access, Lazy Load Heavy Object, Remote Object Access, Add Caching Or Logging

The Proxy pattern provides a surrogate or placeholder for another object to control access to it. The proxy implements the same interface as the real subject, so clients use it transparently, but it intercepts calls to add behavior such as deferring creation, checking permissions, caching results, or routing requests to a remote service. It solves problems where you need an extra layer of control between a client and an object without changing either.

How It Works

A Subject interface is implemented by both the Real Subject (the actual object) and the Proxy. The proxy holds a reference to the real subject (or the information needed to obtain one) and forwards requests to it, performing pre- or post-processing around the delegation. Because both share the Subject interface, the client cannot tell whether it holds the real object or a proxy.

Common variants are: the virtual proxy (creates the expensive real object only when first needed), the protection proxy (enforces access rights), the remote proxy (represents an object in another address space, marshaling calls over the network), the caching proxy (stores results of expensive calls), and the smart reference (adds reference counting, logging, or locking).

When to Use It

Use a proxy when you need controlled, transparent access to an object: to lazily instantiate a heavy object, to guard access by role, to cache expensive operations, to add cross-cutting concerns like logging or rate limiting, or to talk to a remote object as if it were local. Proxies underpin ORMs (lazy-loaded entities), RPC stubs, API gateways, and dynamic-proxy-based aspect frameworks.

Avoid it when the indirection adds latency that matters and the control it provides is not needed, or when a simpler direct call suffices.

Trade-offs

Proxies add control transparently — the client interface is unchanged — and they localize cross-cutting concerns. Virtual proxies improve startup and memory by deferring expensive work; caching proxies improve performance; remote proxies hide network complexity.

The costs include an extra indirection that adds latency and can complicate debugging, the risk of subtle behavioral differences between proxy and real object, and added complexity. Lazy loading via proxies can also cause surprising performance cliffs (for example, ORM N+1 query problems) when access patterns are not anticipated.

Related Patterns

Proxy is structurally identical to Decorator — both wrap an object behind the same interface — but their intent differs: Proxy controls access, while Decorator adds new behavior. It also resembles Adapter, except a proxy keeps the same interface rather than changing it, and Facade, which simplifies a subsystem rather than standing in for one object.

Example

An ORM returns an Order whose getLineItems() is backed by a virtual proxy. The line items are not fetched when the order loads; the first call to getLineItems() triggers a database query, and the result is cached on the proxy. A protection proxy might additionally wrap an AdminService so that each method checks the caller's role before delegating, centralizing authorization without touching the service implementation.