Proxy Pattern — Core Concepts
What a proxy does
A proxy is a stand-in for another object. It implements the same interface as the real object, so callers can’t tell the difference. But internally, the proxy adds behavior before or after forwarding calls to the real object.
This is a structural pattern — it doesn’t change what the object does, but it changes how and when access happens.
Types of proxies
Virtual proxy (lazy initialization)
Delays creating the real object until it’s actually needed. Useful when the object is expensive to create (large file, database connection, ML model) but might not be used in every code path.
Protection proxy (access control)
Checks whether the caller has permission before forwarding the request. This centralizes authorization logic in one place instead of scattering permission checks throughout the codebase.
Caching proxy (smart reference)
Stores results from the real object and returns cached values for repeated requests. This avoids redundant computation or network calls.
Logging proxy
Records every interaction with the real object — useful for debugging, auditing, or performance monitoring.
Remote proxy
Represents an object that lives on another machine. The proxy handles serialization, network communication, and deserialization transparently. Python’s XML-RPC and gRPC stubs are remote proxies.
Proxy vs decorator vs adapter
These patterns all wrap objects, but their purposes differ:
| Pattern | Same interface? | Purpose |
|---|---|---|
| Proxy | Yes | Controls access to the wrapped object |
| Decorator | Yes | Adds new behavior to the wrapped object |
| Adapter | No | Converts one interface to another |
A proxy controls whether and when you access something. A decorator enhances what happens when you access it. The line between proxy and decorator is thin — in practice, a caching proxy could also be called a caching decorator. The intent matters more than the label.
When to use proxies
- Object initialization is expensive and might not be needed
- You need access control without modifying the original class
- Repeated calls return the same result and can be cached
- You want to add logging or metrics without touching the original code
- The real object is remote and needs a local representative
When to avoid proxies
- The real object is cheap to create and always used — a proxy adds complexity without value
- The interface is simple enough that adding a proxy doubles the code for no benefit
- You control both the caller and the object — just modify the object directly
Common misconception
“Proxies are just wrappers.” Every proxy wraps something, but not every wrapper is a proxy. The defining feature is interface identity — a proxy must have the same interface as the real object. If callers need to change their code to use the proxy, it’s not really a proxy.
The one thing to remember: A proxy is a transparent stand-in that adds control — lazy loading, caching, access checks, or logging — without the rest of your code knowing the real object isn’t there directly.
See Also
- Python Adapter Pattern How Python's Adapter Pattern works like a travel power plug — making incompatible things work together.
- Python Bridge Pattern Why separating what something does from how it does it keeps your Python code from becoming a tangled mess.
- Python Builder Pattern Why building complex Python objects step by step beats cramming everything into one giant constructor.
- Python Composite Pattern How the Composite Pattern lets you treat a group of things the same way you'd treat a single thing in Python.
- Python Facade Pattern How the Facade Pattern gives you one simple button instead of a confusing control panel in Python.