代理模式
什么是代理模式?
代理模式是一种结构型设计模式,它允许你提供一个实际对象的替代品或占位符。代理控制着对原对象的访问,并允许在将请求提交给原对象之前或之后执行某些操作。
简单来说,就像现实生活中的"中介"或"代理人"。比如:
-
你想买火车票,不去火车站(原对象),而是去代售点(代理)。
-
明星(原对象)通常有经纪人(代理)来处理事务。
-
你想访问一个国外的网站(原对象),可能需要通过一个代理服务器(代理)。
代理模式的核心思想:
客户端并不直接与实际服务的对象打交道,而是通过一个代理对象。这个代理对象看起来和实际服务的对象一样(通常实现相同的接口),但它内部会控制对实际对象的访问。
代理模式的参与者:
-
Subject (抽象主题角色):
-
定义了真实主题和代理主题的共同接口。
-
这样,在任何使用真实主题的地方都可以使用代理主题。
-
通常是一个接口或抽象类。
-
-
RealSubject (真实主题角色):
-
定义了代理所代表的真实对象。
-
是真正执行业务逻辑的类。
-
-
Proxy (代理主题角色):
-
包含对真实主题的引用(聚合关系)。
-
它实现了抽象主题接口,所以可以替代真实主题。
-
代理角色负责在请求传递给真实主题之前或之后执行额外的操作,或者自己处理一些请求而不传递给真实主题。
-
-
Client (客户端):
-
通过抽象主题接口与代理对象交互。
-
客户端通常不知道它是在与代理对象还是真实对象交互。
-
图示(简化版):
+-----------+ uses +-----------+
| Client |--------------->| Subject |
+-----------+ (Interface) +-----------+
^
| (implements)
+-----------+-----------+
| |
+-----------+ +-------------+
| Proxy |---------->| RealSubject |
+-----------+ (holds a +-------------+
(controls access to) reference)
为什么使用代理模式?(优点)
-
控制访问: 代理可以控制对真实对象的访问权限,例如进行身份验证或权限检查。
-
增加额外功能: 可以在不修改真实对象代码的情况下,为真实对象添加额外的功能,如日志记录、缓存、事务管理等。这符合开闭原则。
-
延迟加载 (Lazy Initialization) / 虚拟代理: 如果真实对象的创建或加载非常耗时,代理可以在真正需要时才创建或加载它,从而提高应用启动速度和性能。
-
远程代理: 代理可以代表一个位于远程地址空间的对象,对客户端隐藏网络通信的复杂性。
-
智能引用: 当真实对象被访问时,代理可以执行一些附加操作,如计算真实对象的引用次数,当没有引用时,可以自动释放它。
常见的代理模式类型:
-
远程代理 (Remote Proxy):
-
为一个位于不同地址空间的对象提供一个本地的代表。
-
例如,Java RMI 中的 Stub 对象就是客户端的远程代理。客户端调用 Stub 的方法,Stub 负责网络通信,将请求转发给服务器端的真实对象。
-
-
虚拟代理 (Virtual Proxy):
-
根据需要创建开销很大的对象。
-
例如,一个图片查看器程序,可以先显示一个占位符(虚拟代理),当用户真正要查看大图时,才加载真实的图片对象。
-
-
保护代理 (Protection Proxy):
-
控制对原始对象的访问。
-
用于对象应该具有不同访问权限的情况。例如,根据用户角色决定是否允许执行某个操作。
-
-
智能引用代理 (Smart Reference Proxy):
-
当对象被引用时,提供一些额外的操作,例如:
-
记录对象的引用次数。
-
在第一次引用一个持久对象时,将它装入内存。
-
在访问一个实际对象前,检查是否已经锁上了它,以确保其他对象不能改变它。
-
-
-
缓存代理 (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
缺点:
-
增加类的数量: 引入代理对象,可能会导致系统中的类数量增加。
-
增加一层间接性: 由于在客户端和真实主题之间增加了代理对象,会引入一定的间接性,可能会造成请求处理速度变慢(虽然通常这种影响很小,且常常被代理带来的好处所抵消)。
总结:
代理模式是一种非常实用和灵活的设计模式,它通过引入一个代理对象来间接访问目标对象,从而可以在不改变目标对象代码的前提下,增加额外的控制和功能。选择哪种类型的代理取决于具体的需求场景。