Structural pattern
Proxy
A proxy stands in for another object, exposing the same interface while controlling — or deferring — access to the real thing.
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));
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();
}
}
Variants: virtual (lazy init, above), protection (auth checks), remote (network stub), caching (memoize results).
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.
Tip: click the same image twice — the second click never touches disk again.
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.