Structural Pattern

Proxy

Provide a surrogate or placeholder for another object to control access to it.

A stand-in object.

The Proxy pattern puts a stand-in in front of a real object. The proxy has the same interface as the real thing, so callers can't tell the difference — but the proxy can do extra work first, like loading the real object only when needed, checking permissions, or caching results.

bad.py
# Problem: every call loads a huge image from disk — even if unused.
class Image:
    def __init__(self, path):
        self.pixels = load_from_disk(path)  # expensive, always

gallery = [Image(p) for p in paths]         # loads ALL up front
good.py
# Fix: Virtual Proxy — stand-in that loads the real object on demand.
class Image:
    def __init__(self, path):
        self.path = path
        self._pixels = None

    def display(self):
        if self._pixels is None:
            # Lazy load: pay the cost only when first needed.
            self._pixels = load_from_disk(self.path)
        show(self._pixels)

gallery = [Image(p) for p in paths]         # cheap handles
gallery[0].display()                        # loads only this one

Kinds of proxies.

The proxy implements the same Subject interface as the RealSubject and holds a reference to it, forwarding calls after doing its own logic. Common types:

  • Virtual proxy — delays creating an expensive object until first use (lazy loading).
  • Protection proxy — enforces access control/permissions.
  • Remote proxy — represents an object in another address space (RPC/stubs).
  • Caching/smart proxy — caches results or adds reference counting, logging, locking.

Relations and real uses.

  • vs Decorator — structurally similar (same interface, wraps an object), but Decorator adds behavior while Proxy controls access; a proxy often manages the lifecycle of its subject, a decorator does not.
  • vs Adapter — Adapter changes the interface; Proxy keeps it identical.
  • Real uses — ORM lazy-loading proxies (Hibernate), Java dynamic proxies / Spring AOP, gRPC/RMI stubs, mocking frameworks, and virtual-memory-style on-demand loading.
  • Cost — indirection and potential surprises (a call may trigger network/DB work or fail lazily).