(2024.06.02 Sun)
代理模式是结构模式(structural pattern)的一种,为真实对象提供了代替用的接口,扮演中介(intermediary)、代理(surrogate)的角色。代理接收到对真实对象的访问/请求,执行与真实对象相同的功能和动作,可加入额外的功能且不需要修改真实对象。
使用场景
若干可能使用代理模式的场景:
- 真实对象加载开销过大(expensive object loading):处理复杂或资源密集型(resource-intensive)对象时,使用虚拟代理(virtual proxy),可扮演placeholder的角色,仅在使用时加载对象的全部(load the full object on demand),优化资源使用
- 远程对象访问:对象在其他地址空间,用户需要以访问本地资源的方式访问,适合建立远程代理(remote proxy),用于管理连接技术细节(如建立网络连接、序列化serialisation、去序列化),使得远程对象用起来像本地对象
- 增强安全性:保护代理(protection proxy)可增强接入控制,比如加入客户端的授权和验证(authorisation & authentication),保护敏感资源等
- 智能引用(smart reference):释放不再使用的重型对象(heavyweight object)场景,该代理跟踪活跃用户并周期检查,如果没有客户端使用对象,代理则释放对象和系统资源。也监测对对象的修改,允许在不修改的情况下重用
案例
概念案例 conceptual example
该案例用于解释
- 代理模式的由哪些类组成
- 每个类扮演什么角色
- 模式间的元素如何关联
首先定义一个Subject
接口,声明RealSubject
和Proxy
两个类的基本操作。只要使用了该接口的RealSubject
类在客户端可用,用户就可以使用Proxy
类代替RealSubject
类。
from abc import ABC, abstractmethod
class Subject(ABC):
@abstractmethod
def request(self) -> None:
pass
接下来定义RealSubject
类,该类中包含核心业务逻辑。而Proxy
类与RealSubject
有相同接口,可以解决RealSubject
中或反应慢或敏感的问题,比如矫正输入数据等。Proxy
中最常见的应用包括惰性加载(lazy loading),缓存,控制连接(controlling the access),记录日志等。Proxy
可执行应用中的一个,并根据结果进行RealSubject
相同的方法
class RealSubject(Subject):
def request(self) -> None:
print("RealSubject: Handling request.")
class Proxy(Subject):
def __init__(self, real_subject: RealSubject) -> None:
self._real_subject = real_subject
def request(self) -> None:
if self.check_access():
self._real_subject.request()
self.log_access()
def check_access(self) -> bool:
print("Proxy: Checking access prior to firing a real request.")
return True
def log_access(self) -> None:
print("Proxy: Logging the time of request.", end="")
下面是客户单代码,客户端通过Subject
接口和所有对象交互。
def client_code(subject: Subject) -> None:
subject.request()
下面运行这些代码
if __name__ == "__main__":
print("Client: Executing the client code with a real subject:")
real_subject = RealSubject()
client_code(real_subject)
print("Client: Executing the same client code with a proxy:")
proxy = Proxy(real_subject)
client_code(proxy)
运行结果如下
Client: Executing the client code with a real subject:
RealSubject: Handling request.
Client: Executing the same client code with a proxy:
Proxy: Checking access prior to firing a real request.
RealSubject: Handling request.
Proxy: Logging the time of request.
数据库缓存 Caching Proxy for Database Queries
(2024.06.03 Mon at KLN)
该案例中,CacheProxy
设计成为客户端与数据库对象RealDatabaseQuery
之间的媒介,检测查询结果是否已经进入缓存,并在规定的缓存周期内(specified cache duration)返回缓存结果。
如果缓存已经过期或结果不在缓存中,CacheProxy
代理从RealDatabaseQuery
中执行查询,将结果推入缓存,并返回给客户端。CacheProxy
降低了数据库服务器的负载,通过缓存的方式加速了数据查询的速度和效率。
首先定义数据库接口DatabaseQuery
,定义了抽象类的基本方法。
from abc import ABC, abstractmethod
import time
class DatabaseQuery(ABC):
@abstractmethod
def execute_query(self, query: str):
pass
定义数据库类RealDatabaseQuery
class RealDatabaseQuery(DatabaseQuery):
def execute_query(self, query: str):
print(f"Execute query: {query}")
# do something
return f"results for query: {query}"
定义数据查询的代理CacheProxy
class CacheProxy(DatabaseQuery):
def __init__(self, real_database_query: DatabaseQuery, cache_duration: int):
self._real_database_query = real_database_query
self._cache_duration = cache_duration
self._cache = {}
def execute_query(self, query: str) -> Union[str, int]:
if query in self._cache and time.time() - self._cache[query]["timestamp"] <= self._cache_duration:
# results in cache
print(f"Returning cached results: {query}")
return self._cache[query]["result"]
else:
# perform query in RealDatabaseQuery
result = self._real_database_query.execute_query(query)
self._cache[query] = {"result": result, "timestamp": time.time()}
return result
客户端代码
if __name__ == "__main__":
real_database_query = RealDatabaseQuery()
cache_proxy = CacheProxy(real_database_query, 3)
# query for the 1st time, will query real database directly
print(cache_proxy.execute_query("SELECT * FROM table_1")
print(cache_proxy.execute_query("SELECT * FROM table_2")
time.sleep(2)
# query for the 2nd time, will read cache which with shorter response time
print(cache_proxy.execute_query("SELECT * FROM table_1")
print(cache_proxy.execute_query("SELECT * FROM table_2")
性能监控代理 Monitoring Proxy for performance metrics
(2024.06.05 Wed)
该代理用于对真实对象的执行性能做统计,记录日志且不会影响真实对象的功能。该代理可惰性实例化(lazily instantiate)真实对象,确保系统资源被保留到对象运行之时。
定义接口
from abc import ABC, abstractmethod
from datetime import datetime
class Subject(ABC):
@abstractmethod
def perform_operation(self):
pass
定义真实对象
class RealSubject(Subject):
def perform_operation(self):
"""
The Real Subject's method representing a costly operation.
"""
print("RealSubject: Performing a costly operation...")
定义代理对象
class MonitoringProxy(Subject):
def __init__(self):
self._real_subject = None
def perform_operation(self):
"""
The Proxy's method, which monitors and adds performance metrics.
Lazily instantiates the Real Subject on the first call.
"""
if self._real_subject is None:
print("MonitoringProxy: Lazy loading the RealSubject...")
self._real_subject = RealSubject()
start_time = datetime.now()
print(
f"MonitoringProxy: Recording operation start time - {start_time}"
)
# Delegate the operation to the Real Subject
self._real_subject.perform_operation()
end_time = datetime.now()
execution_time = end_time - start_time
print(f"MonitoringProxy: Recording operation end time - {end_time}")
print(
f"MonitoringProxy: Operation executed in {execution_time} seconds"
)
定义客户端
if __name__ == "__main__":
monitoring_proxy = MonitoringProxy()
monitoring_proxy.perform_operation()
当代理对象调用perform_operation
方法时,MonitoringProxy
记录运行的开始和结束时间,以计算总运行时间,且无需改变RealSubject
对象的行为。
其他应用案例
-
Virtual Proxy for Image Loading: In photo editing apps, a Virtual Proxy represents high-resolution images, loading them on demand, optimizing memory, and enhancing startup times.
-
Remote Proxy for Web Services: When dealing with remote web services or APIs, a Remote Proxy simplifies communication, handling authentication, encryption, and network connectivity details.
-
Lazy Loading for Expensive Objects: In large datasets or complex objects, use a Proxy to delay object creation until necessary, common in CAD software.
-
Access Control with Security Proxy: Ensure secure data access by employing a Security Proxy, enforcing permissions and authentication checks.
-
Logging Proxy for Debugging: Debugging is streamlined with a Logging Proxy recording method calls and parameters, aiding issue diagnosis without code modifications.
-
Counting Proxy for Resource Management: In resource-critical systems like concurrent programming, a Counting Proxy tracks object references, releasing them when no longer required.
-
Load Balancing with Proxy Servers: Proxy servers in web apps distribute network requests across backend servers, ensuring availability and load balancing.
-
Transactional Proxy for Database Operations: Database apps benefit from a Transactional Proxy handling transactions, ensuring data integrity through rollback and commit mechanisms.
Reference
1 medium,Design Patterns in Python: Proxy, The Powerful Surrogate, by Amir Lavasani
2 Refactoring Guru, Proxy in Python
网友评论