在很多系统中,我们并不会直接访问某个对象,而是通过一个"中间层"来完成访问。
例如:
- 访问远程服务(RPC)
- 数据库懒加载
- 权限控制
- 缓存代理
- 日志记录
客户端看起来是在访问对象本身,但实际上中间有一层"代理"。
这就是 代理模式(Proxy)。
一、代理模式解决什么问题?
一句话:
为一个对象提供一个替身(代理),以控制对该对象的访问。
关键词:
- 控制访问
- 延迟加载
- 权限控制
- 远程访问
代理对象与真实对象实现 相同接口,客户端并不知道自己访问的是代理还是原对象。
二、一个典型场景:图片懒加载
假设系统中需要加载图片,但图片文件很大:
- 加载速度慢
- 占用内存
- 可能根本不会被使用
如果直接创建对象:
python
class RealImage:
def __init__(self, filename):
self.filename = filename
self.load_from_disk()
def load_from_disk(self):
print(f"加载图片: {self.filename}")
def display(self):
print(f"显示图片: {self.filename}")
创建对象时就会立即加载:
python
image = RealImage("photo.jpg")
如果系统中有很多图片,这会严重影响性能。
三、引入代理对象
我们可以创建一个代理类,在真正需要时才加载图片。
python
class ImageProxy:
def __init__(self, filename):
self.filename = filename
self.real_image = None
def display(self):
if self.real_image is None:
self.real_image = RealImage(self.filename)
self.real_image.display()
使用方式:
python
image = ImageProxy("photo.jpg")
# 此时并不会加载图片
image.display()
输出:
加载图片: photo.jpg
显示图片: photo.jpg
图片只有在 真正使用时才加载。
这就是 虚拟代理(Virtual Proxy)。
四、代理模式结构
代理模式通常包含三个角色:
1️⃣ Subject(抽象接口)
定义真实对象与代理对象的共同接口。
2️⃣ RealSubject(真实对象)
真正执行业务逻辑的对象。
3️⃣ Proxy(代理对象)
控制对真实对象的访问。
结构示意:
Client
|
Proxy
|
RealSubject
客户端只依赖接口,不关心具体实现。
五、Python 版本的完整示例
先定义统一接口:
python
from abc import ABC, abstractmethod
class Image(ABC):
@abstractmethod
def display(self):
pass
真实对象:
python
class RealImage(Image):
def __init__(self, filename):
self.filename = filename
self.load()
def load(self):
print(f"加载图片: {self.filename}")
def display(self):
print(f"显示图片: {self.filename}")
代理对象:
python
class ImageProxy(Image):
def __init__(self, filename):
self.filename = filename
self.real_image = None
def display(self):
if not self.real_image:
self.real_image = RealImage(self.filename)
self.real_image.display()
客户端:
python
image = ImageProxy("test.jpg")
print("对象创建完成")
image.display()
运行结果:
对象创建完成
加载图片: test.jpg
显示图片: test.jpg
可以看到:
- 对象创建时没有加载图片
- 第一次访问才真正创建对象
六、代理模式常见类型
代理模式有很多变种,常见的包括:
1 虚拟代理(Virtual Proxy)
控制资源加载,例如:
- 图片懒加载
- 大对象初始化
2 远程代理(Remote Proxy)
代表远程对象,例如:
- RPC 调用
- 微服务客户端
- gRPC
客户端看起来像调用本地对象,其实是远程调用。
3 保护代理(Protection Proxy)
控制访问权限,例如:
- 用户权限校验
- API 权限控制
示例:
python
if not user.is_admin:
raise PermissionError
4 缓存代理(Cache Proxy)
在访问真实对象前增加缓存。
例如:
- Redis 缓存
- API 缓存
- 查询缓存
七、代理模式 vs 装饰器模式
很多人会混淆这两个模式。
| 对比 | 代理模式 | 装饰器模式 |
|---|---|---|
| 目的 | 控制访问 | 增强功能 |
| 是否强调叠加 | 不强调 | 强调 |
| 使用场景 | 权限、远程、懒加载 | 日志、缓存、监控 |
一句话总结:
代理关注"控制访问",装饰器关注"增强功能"。
八、真实项目中的应用
代理模式在真实项目中非常常见。
例如:
1 ORM 懒加载
在 Django / SQLAlchemy 中:
python
user.posts
第一次访问才会查询数据库。
2 RPC 客户端
调用远程服务:
python
user_service.get_user(1001)
看起来像本地调用,其实是网络请求。
3 缓存代理
python
def get_user(id):
if cache.exists(id):
return cache.get(id)
user = db.query(id)
cache.set(id, user)
return user
缓存层本质上就是代理。
九、代理模式优缺点
优点
- 控制对象访问
- 支持懒加载
- 可以增加权限控制
- 可以加入缓存
缺点
- 增加系统复杂度
- 代理类可能变多
- 调试时层级增加
十、一句话总结
代理模式的核心是:为对象增加一个"中间层",控制对对象的访问。
这个中间层可以实现:
- 权限控制
- 懒加载
- 远程调用
- 缓存
在现代系统中,代理模式几乎无处不在。