Card 01/ 09
Proxy Pattern: A Stand-In That Controls Access to an Object
The caller thinks it holds the real object, and the stand-in decides whether, when and how to reach it.
The Proxy pattern places a stand-in object, the proxy, in front of a real object, with the same interface, so that every call from outside passes through the proxy first. The caller cannot tell the two apart, and the proxy decides whether a call reaches the real object, when, and how. This card covers the problems that call for one, the intent, and the four kinds of proxy.
The problems share a shape: access to an object needs control that the object should not hold itself. A high-resolution image takes seconds to load and may never be shown. A document store must check the user's role before a delete. A service runs on another machine. A rate lookup is slow and asked for repeatedly. Putting each of those concerns inside the object mixes it with the object's real job.
Without the patternthe real object checks its own callers
class DocumentStore:
def delete(self, doc_id, user):
if user.role != "editor":
raise PermissionDenied(doc_id)
del self._documents[doc_id]Every method takes a user, and the store cannot be used without a permission model.
With the patternthe stand-in checks, the store stores
store = DatabaseDocumentStore()
documents = ProtectedDocumentStore(store, role)
documents.delete("plan")The caller uses the same interface, and the permission check lives in one place.
The Gang of Four state the intent as: provide a surrogate or placeholder for another object to control access to it. The proxy holds, or knows how to reach, the real object, called the real subject, and passes on each call it allows.
| Kind | What it controls | Example |
|---|---|---|
| Virtual | When the real object is created: only on first use | An image loaded the first time it is drawn |
| Protection | Whether a call is allowed | A delete refused for a user without the editor role |
| Remote | Where the real object is: the proxy forwards the call over the network | A client stub for a service on another machine |
| Caching | Whether a repeated call reaches the real object at all | A rate kept for a minute and returned from memory |
Think of a receptionist who decides who gets through to the manager and when. The analogy breaks in one place: when the manager phones a colleague down the corridor, the call never passes the receptionist, and a real object calling its own methods skips its proxy in the same way.
Proxy is one of seven structural patterns, the family that composes classes and objects into larger structures.
| Pattern | What it does |
|---|---|
| Adapter | Gives an existing object the interface a client expects. |
| Bridge | Separates an abstraction from its implementation so both can vary. |
| Composite | Treats a tree of objects and a single object the same way. |
| Decorator | Adds behaviour by wrapping an object in another with the same interface. |
| Facade | Puts one simple interface in front of a complex subsystem. |
| Flyweight | Shares the common part of many small objects to save memory. |
| Proxy (this topic) | Stands in for an object to control access to it. |
Use it when
- Creating the real object is costly and it may never be used.
- Calls need a check, a cache or a network hop that the real object should not know about.
- The caller must keep using the same interface, unaware of the control in front of it.
Avoid it when
- The control is needed by the object's own internal calls, which a proxy never sees.
- The goal is to add features that stack, which is Decorator.
- The extra layer would hide a cost, such as a network call, that the caller needs to know about.
← → move through sections and cards