Proxy Pattern
The Proxy pattern provides a substitute for another object to control access to it. Use it for lazy initialization, access control, caching, or remote invocation — situations where you want to intercept calls without changing the real object.
Overview
The Proxy pattern places a stand-in object between the caller and the real one. The proxy implements the same interface, so callers don't notice it exists, but it can defer creation, enforce permissions, log calls, cache results, or forward the request over the network. JavaScript even ships a built-in Proxy for the same idea at the language level.
When to use it
- The real object is expensive to create and you want lazy initialization (virtual proxy).
- You need to enforce permissions or audit access before delegating (protection proxy).
- The real object lives elsewhere — another process, another service — and you want a local stand-in (remote proxy).
Example
interface Image {
display(): void;
}
class RealImage implements Image {
constructor(private readonly filename: string) {
console.log(`Loading ${filename} from disk`);
}
display() { console.log(`Showing ${this.filename}`); }
}
class ImageProxy implements Image {
private real?: RealImage;
constructor(private readonly filename: string) {}
display() {
if (!this.real) this.real = new RealImage(this.filename);
this.real.display();
}
}
const image: Image = new ImageProxy("photo.png");
image.display(); // loads + shows
image.display(); // only showsPros
- Adds behavior (lazy load, auth, cache) without changing the real object or the client.
- The real object stays simple — cross-cutting concerns live in the proxy.
- Enables remote and distributed designs without leaking transport details.
Cons
- An extra layer that the team has to know about when debugging.
- If the proxy logic gets complex (cache invalidation, retries), it can drift from the real object's contract.
- Multiple chained proxies are easy to write but painful to reason about.