设计模式-代理模式

代理模式

什么是代理模式?

代理模式是一种结构型设计模式,它允许你提供一个实际对象的替代品或占位符。代理控制着对原对象的访问,并允许在将请求提交给原对象之前或之后执行某些操作。

简单来说,就像现实生活中的"中介"或"代理人"。比如:

  • 你想买火车票,不去火车站(原对象),而是去代售点(代理)。

  • 明星(原对象)通常有经纪人(代理)来处理事务。

  • 你想访问一个国外的网站(原对象),可能需要通过一个代理服务器(代理)。

代理模式的核心思想:

客户端并不直接与实际服务的对象打交道,而是通过一个代理对象。这个代理对象看起来和实际服务的对象一样(通常实现相同的接口),但它内部会控制对实际对象的访问。

代理模式的参与者:

  1. Subject (抽象主题角色):

    • 定义了真实主题和代理主题的共同接口。

    • 这样,在任何使用真实主题的地方都可以使用代理主题。

    • 通常是一个接口或抽象类。

  2. RealSubject (真实主题角色):

    • 定义了代理所代表的真实对象。

    • 是真正执行业务逻辑的类。

  3. Proxy (代理主题角色):

    • 包含对真实主题的引用(聚合关系)。

    • 它实现了抽象主题接口,所以可以替代真实主题。

    • 代理角色负责在请求传递给真实主题之前或之后执行额外的操作,或者自己处理一些请求而不传递给真实主题。

  4. Client (客户端):

    • 通过抽象主题接口与代理对象交互。

    • 客户端通常不知道它是在与代理对象还是真实对象交互。

图示(简化版):

复制代码
+-----------+       uses       +-----------+
|  Client   |--------------->|  Subject  |
+-----------+                  (Interface) +-----------+
                                   ^
                                   | (implements)
                       +-----------+-----------+
                       |                       |
               +-----------+           +-------------+
               |   Proxy   |---------->| RealSubject |
               +-----------+  (holds a  +-------------+
(controls access to)  reference)

为什么使用代理模式?(优点)

  1. 控制访问: 代理可以控制对真实对象的访问权限,例如进行身份验证或权限检查。

  2. 增加额外功能: 可以在不修改真实对象代码的情况下,为真实对象添加额外的功能,如日志记录、缓存、事务管理等。这符合开闭原则。

  3. 延迟加载 (Lazy Initialization) / 虚拟代理: 如果真实对象的创建或加载非常耗时,代理可以在真正需要时才创建或加载它,从而提高应用启动速度和性能。

  4. 远程代理: 代理可以代表一个位于远程地址空间的对象,对客户端隐藏网络通信的复杂性。

  5. 智能引用: 当真实对象被访问时,代理可以执行一些附加操作,如计算真实对象的引用次数,当没有引用时,可以自动释放它。

常见的代理模式类型:

  1. 远程代理 (Remote Proxy):

    • 为一个位于不同地址空间的对象提供一个本地的代表。

    • 例如,Java RMI 中的 Stub 对象就是客户端的远程代理。客户端调用 Stub 的方法,Stub 负责网络通信,将请求转发给服务器端的真实对象。

  2. 虚拟代理 (Virtual Proxy):

    • 根据需要创建开销很大的对象。

    • 例如,一个图片查看器程序,可以先显示一个占位符(虚拟代理),当用户真正要查看大图时,才加载真实的图片对象。

  3. 保护代理 (Protection Proxy):

    • 控制对原始对象的访问。

    • 用于对象应该具有不同访问权限的情况。例如,根据用户角色决定是否允许执行某个操作。

  4. 智能引用代理 (Smart Reference Proxy):

    • 当对象被引用时,提供一些额外的操作,例如:

      • 记录对象的引用次数。

      • 在第一次引用一个持久对象时,将它装入内存。

      • 在访问一个实际对象前,检查是否已经锁上了它,以确保其他对象不能改变它。

  5. 缓存代理 (Caching Proxy):

    • 为开销大的运算结果提供暂时存储。它可以允许多个客户端共享结果,以减少计算或网络延迟。

    • 例如,网页代理服务器缓存常访问的网页。

一个简单的虚拟代理例子 (Python 伪代码):

复制代码
from abc import ABC, abstractmethod
​
# 1. Subject (抽象主题)
class Image(ABC):
    @abstractmethod
    def display(self):
        pass
​
# 2. RealSubject (真实主题)
class RealImage(Image):
    def __init__(self, filename: str):
        self._filename = filename
        self._load_from_disk() # 模拟耗时操作
​
    def _load_from_disk(self):
        print(f"Loading image: {self._filename} from disk...")
        # 模拟加载时间
        import time
        time.sleep(2)
​
    def display(self):
        print(f"Displaying image: {self._filename}")
​
# 3. Proxy (代理主题) - 虚拟代理
class ProxyImage(Image):
    def __init__(self, filename: str):
        self._filename = filename
        self._real_image: RealImage = None # 真实对象引用,初始为None
​
    def display(self):
        if self._real_image is None: # 延迟加载
            print("Proxy: RealImage not loaded yet. Creating it now.")
            self._real_image = RealImage(self._filename)
        self._real_image.display()
​
# 4. Client (客户端)
if __name__ == "__main__":
    print("Creating proxy image objects (not loading yet)...")
    image1 = ProxyImage("photo1.jpg")
    image2 = ProxyImage("photo2.png")
​
    print("\nClient: Requesting to display image1 for the first time:")
    image1.display() # 此时会加载 RealImage
​
    print("\nClient: Requesting to display image1 again:")
    image1.display() # 此时直接使用已加载的 RealImage
​
    print("\nClient: Requesting to display image2 for the first time:")
    image2.display() # 此时会加载 RealImage

缺点:

  • 增加类的数量: 引入代理对象,可能会导致系统中的类数量增加。

  • 增加一层间接性: 由于在客户端和真实主题之间增加了代理对象,会引入一定的间接性,可能会造成请求处理速度变慢(虽然通常这种影响很小,且常常被代理带来的好处所抵消)。

总结:

代理模式是一种非常实用和灵活的设计模式,它通过引入一个代理对象来间接访问目标对象,从而可以在不改变目标对象代码的前提下,增加额外的控制和功能。选择哪种类型的代理取决于具体的需求场景。

相关推荐
杨充21 小时前
10.可测试性实战设计
设计模式·开源·代码规范
杨充21 小时前
9.重构十二式的实战
设计模式·开源·代码规范
杨充21 小时前
6.设计原则的全景图
设计模式·开源·全栈
杨充21 小时前
2.面向对象的特性
设计模式
杨充21 小时前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充21 小时前
8.反模式与坏味道
设计模式·开源·代码规范
杨充1 天前
3.接口vs抽象类比较
设计模式
咖啡八杯1 天前
文法、BNF与AST
java·设计模式·解释器模式·ast·文法
咖啡八杯1 天前
GoF设计模式——解释器模式
java·后端·spring·设计模式