Skip to content

Proxy

Proxy provides a surrogate or placeholder for another object that shares its interface and controls access to it, so the proxy can decide whether, when, and how the real object is reached.

Sometimes you want to use an object, but accessing it directly is expensive, sensitive, or remote. A high-resolution image should not load from disk until something actually displays it. A privileged operation should run only for an authorised caller. A remote service should be reachable through a local stand-in. In each case the real object is fine; what you need is a layer of control around getting to it.

Proxy is that layer. It implements the same interface as the real subject, so clients cannot tell the difference, and holds a reference to (or knows how to obtain) the real object. On each call the proxy can do work first — lazily create the subject, check permissions, cache a result, log the access — and then forward to the real object, or refuse. Because the proxy is interchangeable with the subject, you slip this control in without changing any client code.

classDiagram
  class Image {
    <<interface>>
    +display() string
  }
  class RealImage {
    -file: string
    +display() string
  }
  class LazyImageProxy {
    -file: string
    -real: RealImage
    +display() string
  }
  class Client
  Image <|.. RealImage
  Image <|.. LazyImageProxy
  LazyImageProxy --> RealImage : creates and forwards on demand
  Client --> Image : uses
A proxy shares the subject's interface and controls access to the real object
  • Subject (Image) — the interface shared by the real object and its proxy, so they are interchangeable.
  • Real Subject (RealImage) — the actual object that does the work and is expensive or sensitive to reach.
  • Proxy (LazyImageProxy) — implements the subject interface, controls access, and forwards to the real subject when appropriate.
  • Client — talks to the subject interface and is unaware whether it holds a proxy or the real object.

A virtual proxy that delays loading a heavy image until the first display call, then caches the loaded object for reuse.

interface Image {
display(): string;
}
class RealImage implements Image {
constructor(private readonly file: string) {
// Pretend this reads a large file from disk.
console.log(`loading ${file}`);
}
display = () => `showing ${this.file}`;
}
class LazyImageProxy implements Image {
private real: RealImage | null = null;
constructor(private readonly file: string) {}
display(): string {
if (this.real === null) {
this.real = new RealImage(this.file); // load only on first use
}
return this.real.display();
}
}
const image: Image = new LazyImageProxy('photo.png'); // nothing loaded yet
console.log(image.display()); // loading photo.png \n showing photo.png
console.log(image.display()); // showing photo.png (cached, no reload)

All three wrap something, but their purposes diverge. A Decorator shares the subject’s interface and adds behaviour, enriching the result of each call; you stack decorators to compose features. A Proxy also shares the subject’s interface but controls access — it decides whether the real call happens at all (lazy loading, permission checks, caching) rather than enhancing what comes back, and it typically manages the lifecycle of the one real subject. A Facade does neither: it invents a new, simpler interface over a whole subsystem of many classes, so it does not share the subject’s interface and is not interchangeable with any single underlying object.

  • Pro: controls access transparently — lazy loading, access control, caching, or remoting — without changing clients.
  • Pro: defers or avoids expensive work until it is actually needed (virtual proxy).
  • Pro: centralises cross-cutting concerns like authorisation or logging in one place.
  • Con: adds a layer of indirection that can obscure where work or latency really happens.
  • Con: a lazy or remote proxy introduces timing surprises — the first call behaves very differently from later ones.
  • Decorator shares the wrapping shape but adds behaviour rather than controlling access.
  • Facade simplifies a subsystem behind a new interface, whereas a proxy keeps the same interface as one subject.
  • Adapter changes an interface to match a client; a proxy deliberately keeps the interface identical.
What is the main purpose of a Proxy?
What does a virtual proxy (like the lazy image) optimise?
How does a Proxy differ from a Decorator?
Why is a Proxy interchangeable with the real subject?