美文网首页
代理模式Proxy Pattern, since 2024-06

代理模式Proxy Pattern, since 2024-06

作者: Mc杰夫 | 来源:发表于2024-06-01 14:23 被阅读0次

    (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接口,声明RealSubjectProxy两个类的基本操作。只要使用了该接口的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

    相关文章

      网友评论

          本文标题:代理模式Proxy Pattern, since 2024-06

          本文链接:https://www.haomeiwen.com/subject/vmcjqjtx.html