The Proxy Pattern is a structural design pattern that allows you to provide a substitute or placeholder for another object. A proxy controls access to the original object, allowing you to perform something either before or after the request gets to the original object.
1. Introduction
The Proxy Pattern introduces an intermediary object, the “proxy,” that acts as an interface to another object, known as the “real subject.” This intermediary can add various functionalities, such as access control, caching, lazy initialization, or logging, without modifying the real subject’s core logic.
Common use cases include:
- Protection Proxy: Controls access to the real subject based on permissions.
- Virtual Proxy: Creates expensive objects on demand (lazy loading).
- Remote Proxy: Provides a local representation of an object that is in a different address space (e.g., in a remote server).
- Logging Proxy: Logs requests to the real subject.
- Caching Proxy: Caches results from the real subject to improve performance.
2. Implementation
Consider a scenario where you want to log access to a sensitive report.
# 1. Subject Interface
class IReportGenerator:
def generate_report(self):
raise NotImplementedError
# 2. Real Subject
class RealReportGenerator(IReportGenerator):
def generate_report(self):
return "Sensitive financial report data."
# 3. Proxy
class ReportGeneratorProxy(IReportGenerator):
def __init__(self, real_report_generator: RealReportGenerator, user_role: str):
self._real_report_generator = real_report_generator
self._user_role = user_role
def generate_report(self):
# Access control / Protection Proxy
if self._user_role == "Admin" or self._user_role == "Auditor":
print(f"Log: User with role '{self._user_role}' accessing report.")
return self._real_report_generator.generate_report()
else:
print(f"Log: User with role '{self._user_role}' denied access to report.")
return "Access Denied: You do not have permission to view this report."
# Client Code
if __name__ == "__main__":
real_report = RealReportGenerator()
# User with Admin role
admin_proxy = ReportGeneratorProxy(real_report, "Admin")
print(admin_proxy.generate_report())
print("-" * 30)
# User with Viewer role
viewer_proxy = ReportGeneratorProxy(real_report, "Viewer")
print(viewer_proxy.generate_report())
print("-" * 30)
# User with Auditor role
auditor_proxy = ReportGeneratorProxy(real_report, "Auditor")
print(auditor_proxy.generate_report())
Explanation:
IReportGenerator
defines the common interface for both the real subject and the proxy.RealReportGenerator
is the actual object containing the sensitive report generation logic.ReportGeneratorProxy
acts as a proxy, intercepting thegenerate_report
call. It adds logic to check theuser_role
before delegating the call to theRealReportGenerator
, effectively implementing a protection proxy.
3. Mermaid Diagram
graph TD; A[Client] --> B[ReportGeneratorProxy]; B --> C[RealReportGenerator];
The client interacts with the ReportGeneratorProxy
, which then conditionally forwards requests to the RealReportGenerator
.
4. Pros & Cons
Advantages:
- Access Control: Can restrict or grant access to the real subject based on certain criteria.
- Lazy Loading: Delays the creation and initialization of an expensive object until it’s actually needed, saving resources.
- Logging/Auditing: Can easily intercept method calls to add logging, auditing, or monitoring capabilities.
- Caching: Can store results of operations from the real subject to improve performance for subsequent requests.
- Remote Communication: Facilitates interaction with objects located in different address spaces.
- Separation of Concerns: Helps separate cross-cutting concerns (e.g., security, logging) from the core business logic of the real subject.
Disadvantages:
- Increased Complexity: Introduces an additional layer of abstraction, which can make the code more complex and harder to understand.
- Potential Performance Overhead: Each operation through the proxy incurs additional processing due to the proxy’s logic, which might be negligible but can add up.
- Delayed Initialization Issues: For virtual proxies, if the real object’s creation fails, the error might occur later than expected, making debugging harder.
5. References
- Gamma, E., Helm, R., Johnson, R., & Vlissides, J. (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley.
- Refactoring.Guru. (n.d.). Proxy Design Pattern. Retrieved from https://refactoring.guru/design-patterns/proxy