Proxy Pattern — Control Access to Objects
What You'll Learn
You will learn how the Proxy pattern controls access to a target object, with variants including virtual proxies for Lazy Loading, protection proxies for access control, and remote proxies for Distributed Systems. You will understand the key differences between Proxy and Decorator.
Why It Matters
Loading a high-resolution image before it is visible wastes bandwidth and memory. Deleting a document without checking permissions risks data loss. Proxies intercept these operations, adding Lazy Loading, access control, logging, or Caching without modifying the real subject. The proxy and the real subject share the same interface, so clients never know whether they are talking to a lightweight placeholder or the actual object.
Real-World Use
Object-Relational Mappers return proxy objects that load related data from the database only when first accessed (Lazy Loading). DodaTech's secret store uses a proxy that encrypts values on write and decrypts on read, keeping the core store agnostic to security concerns. If the encryption algorithm changes, only the proxy is updated — the store itself remains untouched.
The Pattern
Subject declares the common interface. RealSubject is the actual object. Proxy holds a reference to RealSubject and controls access to it.
from abc import ABC, abstractmethod
class Image(ABC):
@abstractmethod
def display(self) -> str:
pass
class HighResImage(Image):
def __init__(self, filename: str):
self._filename = filename
self._load_from_disk()
def _load_from_disk(self):
self._data = f"[Image data for {self._filename}]"
def display(self) -> str:
return f"Displaying {self._filename}: {self._data}"
class ImageProxy(Image):
def __init__(self, filename: str):
self._filename = filename
self._real_image = None
def display(self) -> str:
if self._real_image is None:
self._real_image = HighResImage(self._filename)
return self._real_image.display()
class SecureImageProxy(Image):
def __init__(self, filename: str, user_role: str):
self._real = ImageProxy(filename)
self._user_role = user_role
def display(self) -> str:
if self._user_role != "admin":
return "Access denied: insufficient permissions"
return self._real.display()
guest_view = SecureImageProxy("photo.png", "guest")
admin_view = SecureImageProxy("photo.png", "admin")
print(guest_view.display())
print(admin_view.display())
Access denied: insufficient permissions
Displaying photo.png: [Image data for photo.png]
Structure
classDiagram
class Subject {
<>
+request()
}
class RealSubject {
+request()
}
class Proxy {
-realSubject: RealSubject
+request()
}
RealSubject ..|> Subject
Proxy ..|> Subject
Proxy --> RealSubject : delegates
Real-World Usage
- Python
weakref.proxy— creates a proxy to an object without increasing its reference count. - Java
java.lang.reflect.Proxy— generates dynamic proxy classes for interface-based access control. - Spring AOP — proxies wrap beans to add Transaction management, security, and logging.
- Nginx reverse proxy — a remote proxy that forwards requests to backend servers, adding Load Balancing and Caching.
- ES6 JavaScript
Proxy— built-in proxy object that interceptsget,set,has, and other operations.
Related Patterns
- Decorator adds behaviour; Proxy controls access — structurally similar but semantically different.
- Adapter changes the interface; Proxy preserves the interface.
- Facade simplifies a subsystem; Proxy controls access to a single object.
- Lazy Loading is often implemented with a virtual proxy.
Pros and Cons
| Pros | Cons |
|---|---|
| Separates cross-cutting concerns from business logic | Adds indirection that can complicate debugging |
| Supports Lazy Loading, Caching, and access control | May introduce latency for remote proxies |
| Follows the Open/Closed Principle | Proxies can become tightly coupled to RealSubject interface changes |
| Can be introduced without changing RealSubject code | Overuse leads to excessive proxy layers |
The code shows two proxy variants. ImageProxy is a virtual proxy that delays the expensive image loading until display() is actually called. SecureImageProxy is a protection proxy that checks the user's role before delegating. They can also be combined, as demonstrated — SecureImageProxy wraps ImageProxy, which in turn wraps HighResImage. This layering is a key advantage of the Proxy pattern: each concern is isolated in its own class and can be composed independently.
Practice Questions
- Implement a logging proxy that records every method call made to a service object with timestamps and parameters.
- Compare virtual proxy with lazy initialisation via direct
nullchecks inside the real subject — which is more maintainable? - How would you implement a remote proxy that serialises method calls over HTTP and deserialises responses?
- What are the thread-safety implications of a virtual proxy that caches the RealSubject after first creation?
Challenge
Implement a Caching proxy that stores results of expensive method calls and returns cached values for repeated calls with the same parameters. Include cache invalidation.
Real-World Task
Run DodaTech's call graph tracer on your application to identify frequently-called methods that return stable data. Implement a Caching proxy for those methods and measure the latency improvement.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro