design·lab

Structural pattern

Proxy

A proxy stands in for another object, exposing the same interface while controlling — or deferring — access to the real thing.

✗ The problem

Expensive work happens too early

Creating a HeavyImage loads the full file in its constructor — even for images that are never scrolled into view.

class HeavyImage {
  constructor(file) {
    // runs immediately — even if never shown!
    this.data = loadFromDisk(file);
  }
  display() { /* render this.data */ }
}

// just building the list pays the full cost:
const imgs = files.map(f => new HeavyImage(f));
You pay the full cost up front, even for off-screen images.
✓ The pattern

A stand-in with the same interface

The proxy implements the same display() contract and only creates the real object the first time it's actually needed.

class ImageProxy {
  constructor(file) {
    this.file = file;
    this.real = null;   // not created yet
  }
  display() {
    if (!this.real) {
      this.real = new HeavyImage(this.file);
    }
    return this.real.display();
  }
}
Client
image.display()
Image ‹interface›
display()
↓ implements
ImageProxy
display()
↓ creates on demand
HeavyImage
display()

Variants: virtual (lazy init, above), protection (auth checks), remote (network stub), caching (memoize results).

✓ See it live

Click a proxy — it loads only once

Each thumbnail is an ImageProxy, not yet loaded. Click one to call display(): the first click creates the real HeavyImage; click it again and it's served instantly from the cached instance.

real loads: 0

Tip: click the same image twice — the second click never touches disk again.

✓ Takeaway

Same interface, controlled access

  • A proxy is a stand-in that controls access to the real subject — it implements the same interface, so callers can't tell the difference.
  • Uses: lazy/virtual init, access control, caching, remote calls (RPC stubs, JS Proxy, ORM lazy relations).
  • vs. Decorator: Decorator adds behavior. vs. Adapter: Adapter changes the interface. Proxy keeps the interface and guards access.
  • Careful: every call adds a layer of indirection — and possibly latency.
🎯 Principle applied: Proxy relies on Liskov Substitution (the proxy is interchangeable with the real subject — same interface) and SRP (access concerns like caching, laziness, or auth live in the proxy, not the real object).