结构型------代理模式
代理模式指的是通过创建一个代理来控制对原始对象的访问。代理在客户端与实际对象之间充当"中介"
特点
- 访问控制:代理对象可以控制对实际对象的访问,从而实现对访问权限的控制。
- 延迟加载:代理对象可以在实际对象被调用时才创建,从而实现延迟加载。
- 解耦: 客户端金依赖代理接口,可以与实际对象分离,从而实现解耦。
- 类型多样:虚拟代理(既延迟加载)、缓存代理(缓存对象)、远程代理(远程对象)、保护代理(权限控制)等。
模式结构
角色 | 描述 |
---|---|
抽象主题 (Subject) | 声明真实主题和代理主题的共同接口。客户端依赖此接口 |
真实主题 (RealSubject) | 实现抽象主题定义的接口,并提供其功能实现。实际执行业务逻辑的对象 |
代理主题 (Proxy) | 实现抽象主题定义的接口,并持有真实主题的引用。代理主题控制对真实主题的访问 |
代理模式类型
类型 | 应用场景 |
---|---|
虚拟代理 | 当需要创建一个大开销的对象时,创建一个代理对象,直到需要时才创建真实的对象。 也叫延迟加载(比如在图片加载时,先加载一张占位图,当图片加载完成后再替换占位图)。 |
保护代理 | 控制权限(在客户端对真实对象进行访问前,利用代理进行权限校验等操作)。 |
缓存代理 | 缓存调用结果(为优化性能,将重复计算的值缓存起来,下次使用时直接返回缓存值)。 |
远程代理 | 本地代理远程服务(将本地的请求转发到远程的对象上)。 |
简单示例
1. 虚拟代理
python
from abc import ABC, abstractmethod
# 抽象主题
class Image(ABC):
@abstractmethod
def display(self):
pass
# 真实主题(大开销对象)
class RealImage(Image):
def __init__(self, filename):
self.filename = filename
self.load_from_disk()
def load_from_disk(self):
print(f"Loading {self.filename} from disk")
def display(self):
print(f"Displaying {self.filename}")
# 代理主题(控制加载时机)
class ProxyImage(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()
# 客户端代码
if __name__ == "__main__":
# 创建代理,此时不会实际加载图片
proxy_image = ProxyImage("test_image.jpg") # Loading test_image.jpg from disk
# 当需要展示时,实际去加载显示
proxy_image.display() # Displaying test_image.jpg
2. 保护代理
python
from abc import ABC, abstractmethod
# 抽象主题
class DataBase(ABC):
@abstractmethod
def query(self, sql):
pass
# 真实主题(实现对数据库的查询操作)
class RealDataBase(DataBase):
def query(self, sql):
return f"query result: {sql}"
# 代理(对请求对象进行拦截,权限验证)
class ProxyDataBase(DataBase):
def __init__(self, user):
self.user = user
self.real_db = RealDataBase()
def query(self, sql):
if self._check_access():
return self.real_db.query(sql)
else:
return "access denied"
def _check_access(self):
return self.user == "admin"
# 客户端代码
if __name__ == "__main__":
proxy_db = ProxyDataBase("admin")
print(proxy_db.query("select * from user")) # query result: ....
proxy_db = ProxyDataBase("guest")
print(proxy_db.query("select * from user")) # access denied
3. 缓存代理
python
from abc import ABC, abstractmethod
class Calculate(ABC):
@abstractmethod
def calculate(self, n): pass
class RealCalculate(Calculate):
def calculate(self, n):
return n * n
class ProxyCalculate(Calculate):
def __init__(self):
self.cache = {}
self.real_calculate = RealCalculate()
def calculate(self, n):
if n not in self.cache:
self.cache[n] = self.real_calculate.calculate(n)
return self.cache[n]
if __name__ == '__main__':
proxy_calculate = ProxyCalculate()
for i in range(10):
print(proxy_calculate.calculate(i)) # 第一次计算,缓存
for i in range(10):
print(proxy_calculate.calculate(i)) # 第二次计算,直接从缓存中取
4.远程代理
python
import requests
class BaiduService:
def get_data(self, question): pass
class BaiduServiceProxy(BaiduService):
def __init__(self):
self.question = question
self.baidu_api = "www.baidu.com"
def get_data(self, question):
response = requests.get(self.baidu_api, params={"question": question})
return response.text
# 客户端代码
if __name__ == "__main__":
# 客户端仅与代理交互,无需感知远程服务的存在
baidu_service = BaiduServiceProxy()
data = baidu_service.get_data("如何使用代理模式")
代理模式 VS 外观模式 VS 装饰器模式 VS 适配器模式
维度 | 代理模式 | 外观模式 | 装饰器模式 | 适配器模式 |
---|---|---|---|---|
核心目的 | 控制对象访问 | 简化复杂系统接口 | 动态扩展对象功能 | 接口兼容性转换 |
结构特点 | 代理与真实对象实现相同接口 | 外观类聚合多个子系统接口 | 装饰器模式聚合被装饰对象 | 适配器模式聚合被适配对象 |
对象关系 | 代理对象持有真实对象 | 外观持有多个子系统对象 | 装饰器包裹原始对象 | 适配器持有或继承被适配对象 |
接口一致性 | 保持与真实对象接口一致 | 提供新的简化接口 | 保持与原始对象接口一致 | 转换接口以匹配目标需求 |
扩展方向 | 访问控制逻辑 | 接口简化与整合 | 功能叠加 | 接口适配 |