结构型——代理模式
代理模式指的是通过创建一个代理来控制对原始对象的访问。代理在客户端与实际对象之间充当“中介”
特点
- 访问控制:代理对象可以控制对实际对象的访问,从而实现对访问权限的控制。
- 延迟加载:代理对象可以在实际对象被调用时才创建,从而实现延迟加载。
- 解耦: 客户端金依赖代理接口,可以与实际对象分离,从而实现解耦。
- 类型多样:虚拟代理(既延迟加载)、缓存代理(缓存对象)、远程代理(远程对象)、保护代理(权限控制)等。
模式结构
角色 |
描述 |
抽象主题 (Subject) |
声明真实主题和代理主题的共同接口。客户端依赖此接口 |
真实主题 (RealSubject) |
实现抽象主题定义的接口,并提供其功能实现。实际执行业务逻辑的对象 |
代理主题 (Proxy) |
实现抽象主题定义的接口,并持有真实主题的引用。代理主题控制对真实主题的访问 |
代理模式类型
类型 |
应用场景 |
虚拟代理 |
当需要创建一个大开销的对象时,创建一个代理对象,直到需要时才创建真实的对象。 也叫延迟加载(比如在图片加载时,先加载一张占位图,当图片加载完成后再替换占位图)。 |
保护代理 |
控制权限(在客户端对真实对象进行访问前,利用代理进行权限校验等操作)。 |
缓存代理 |
缓存调用结果(为优化性能,将重复计算的值缓存起来,下次使用时直接返回缓存值)。 |
远程代理 |
本地代理远程服务(将本地的请求转发到远程的对象上)。 |
简单示例
1. 虚拟代理
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")
proxy_image.display()
2. 保护代理
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"))
proxy_db = ProxyDataBase("guest")
print(proxy_db.query("select * from user"))
3. 缓存代理
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.远程代理
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 适配器模式
维度 |
代理模式 |
外观模式 |
装饰器模式 |
适配器模式 |
核心目的 |
控制对象访问 |
简化复杂系统接口 |
动态扩展对象功能 |
接口兼容性转换 |
结构特点 |
代理与真实对象实现相同接口 |
外观类聚合多个子系统接口 |
装饰器模式聚合被装饰对象 |
适配器模式聚合被适配对象 |
对象关系 |
代理对象持有真实对象 |
外观持有多个子系统对象 |
装饰器包裹原始对象 |
适配器持有或继承被适配对象 |
接口一致性 |
保持与真实对象接口一致 |
提供新的简化接口 |
保持与原始对象接口一致 |
转换接口以匹配目标需求 |
扩展方向 |
访问控制逻辑 |
接口简化与整合 |
功能叠加 |
接口适配 |