Proxy
What This Concept Is
Proxy is a structural pattern that places a surrogate in front of another object to control access to it. The proxy implements the same interface as the real subject, so the client cannot tell them apart, and it interposes logic that makes sense for that kind of indirection.
Common kinds:
- Virtual proxy -- defer expensive construction until it is actually needed (lazy loading).
- Remote proxy -- stand in for an object that lives in another process or machine (RPC stubs).
- Protection proxy -- check permissions before forwarding the call.
- Smart reference -- add reference counting, locking, caching, or logging around the real call.
The problem it absorbs: access to a resource needs to be controlled, delayed, remoted, or measured, but you do not want that control to leak into the resource's own code or into every caller.
Why It Matters Here
Proxy sits at the boundary of many real engineering concerns: lazy loading in ORMs, HTTP clients generated from IDL, permission checks, caching layers, client-side stubs, and transparent retry wrappers. The pattern name is also one of the most overloaded words in the industry: reverse proxies, HTTP proxies, and Java dynamic proxies all touch the same idea but at different scales.
This concept page is the OO design pattern: a surrogate object, used in-process, with the same interface as the subject.
Concrete Example
# Subject interface (shared)
class Image:
def display(self): raise NotImplementedError
# Real subject (expensive to construct)
class HighResImage(Image):
def __init__(self, path):
self.path = path
self.pixels = self._load_from_disk() # expensive
def _load_from_disk(self):
print(f"[loading {self.path}]")
return b"...megabytes..."
def display(self):
print(f"[rendering {self.path}]")
# Virtual proxy: same interface, defers construction
class LazyImage(Image):
def __init__(self, path):
self.path = path
self._real = None
def display(self):
if self._real is None:
self._real = HighResImage(self.path)
self._real.display()
# Protection proxy: same interface, checks caller
class RestrictedImage(Image):
def __init__(self, inner: Image, user):
self.inner, self.user = inner, user
def display(self):
if not self.user.may_view_images:
raise PermissionError("not allowed")
self.inner.display()
album = [LazyImage(f"img{i}.jpg") for i in range(1000)]
album[3].display() # only this one is actually loaded
Structural sketch:
Client --> Subject (interface)
^
|
+--------+ +------------+
| Proxy |------->| RealSubject|
+--------+ +------------+
(controls access: lazy / remote / secured / smart)
Common Confusion / Misconception
Decorator vs Proxy is the sharpest confusion here:
- Decorator's intent is "add behavior." Structure: same interface, wraps a component, is typically composed by the client.
- Proxy's intent is "control access." Structure: same interface, wraps a subject, is typically created instead of the subject without the client knowing.
Structurally they look almost identical. The difference is where they are used and why. A logging wrapper used at the composition root to add logging is a Decorator; a wrapper you return instead of the real object to enforce a permission check is a Proxy.
Other confusions:
- The word "proxy" in HTTP land refers to a process-level network component. Same idea, different scale.
- An ORM's lazy-loading wrapper is a virtual proxy; treat it as such when reasoning about N+1 queries.
How To Use It
- Identify the reason you need indirection: laziness, remoteness, protection, measurement.
- Use the real subject's interface; do not invent a different one.
- Keep the proxy minimal -- its whole job is the one access concern, not additional behavior.
- Document which kind of proxy it is; "proxy" alone is too ambiguous in code review.
- Prefer to inject the proxy at the composition root, so the client can be tested against the real subject directly.
Check Yourself
- What single question distinguishes a Proxy from a Decorator?
- Why does the virtual proxy pattern matter in ORM and UI code specifically?
- What is the cost of a protection proxy in test doubles?
Mini Drill or Application
Implement a virtual proxy for lazy image loading:
- A
Imageinterface withdisplay(). - A
HighResImagethat loads its bytes in the constructor (simulate latency). - A
LazyImagethat only constructsHighResImageon firstdisplay(). - A
RestrictedImageprotection proxy that checks a user's permission before delegating.
Then add a short paragraph describing whether you would combine the two proxies, or keep them separate and decide which one wraps which.
Read This Only If Stuck
- Head First: The Proxy Pattern -- Controlling Object Access
- Head First: The Role of the Remote Proxy
- Head First: Get Ready for the Virtual Proxy
- Head First: Designing the Album Cover Virtual Proxy
- Head First: Using Java Proxy to Create a Protection Proxy
- GoF: Proxy -- Implementation
- GoF: Proxy -- Sample Code