什么是 代理模式?
代理模式 通过名字 得知, 是提供 一个第三方 服务 给 客户端使用,这个第三方服务 对接 真实服务,并且 实现了 真实服务 功能,并且还增加了 额外的功能;
生活中的例子
- 12306代理购票(黄牛软件); 此类软件 不仅实现了 购票的功能,而且还增加 了 抢票功能,额外 手续费 功能 等等;
- 4s店 买车; 这种模式 不仅 实现了 选车,试驾,购车服务, 还增加了 额外的 强制性消费 如 手续费,贴膜,上保险 等 恶心服务;
- 双方交易,其中 一方 不想透露身份,通过 委托某中间人 进行代理交易; 这样 起到了 保护 目标对象 的作用;
代码中的例子:
- 在业务系统中,可能需要 调用 数据服务 查询数据,这时 如果每次入参都相同,还是查询数据 会造成 数据服务的压力,这时 可以通过 代理模式 实现 缓存代理服务,此服务 先查询自己服务是否有缓存,没有再 将请求转发到 数据服务;
识别此模式方法:
- 代理模式会将所有实际工作委派给一些其他对象。 除非代理是某个服务的子类, 否则每个代理方法最后都应该引用一个服务对象。
该模式使用场景:
- 远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。
- 虚拟代理,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
- 安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。
- 智能指引,主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它。
- 延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,Hibernate 中就存在属性的延迟加载和关联表的延时加载。
- 日志记录/缓存 代理, 这种单纯的 增加额外功能的 代理方式,可以通过 装饰器模式实现(即使对方是第三方sdk包); 具体使用哪种方式 根据实际情况 和个人喜好 自行选择;
该模式关键的角色:
- 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
- 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
- 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
该模式的主要优缺点如下:
优点:
- 将 客户端 和 目标对象服务 进行分离,某种程度 降低了系统的耦合度;
- 代理对象 拓展了 目标对象的功能;
- 代理模式 在 客户端 和 目标对象 间 起到了 中介和保护目标对象的作用;
- 符合开闭原则;可以 在 不修改 客户端 和 目标对象 基础上 创建 新的代理;
- 可以控制 服务对象 的生命周期进行管理; 如果 没有 客户端 调用时,销毁 目标对象,节约资源;
缺点:
- 服务响应 因为 增加了一层代理,肯定会多执行代码,所以 服务响应会延迟
- 代码结构复杂
- 目标对象 如果 新增了 接口,则 代理对象 也要新增相应接口;
和 其他模式 的 比较:
与 装饰模式 比较:
- 装饰模式 是直接 在原有对象 基础上添加功能;而 代理模式 则是 新建一个 和原来对象 所实现的 接口 相同的 对象,然后 在新对象 上增加额外的功能;
- 装饰器模式 为对象 提供加强的接口; 代理模式 为对象提供相同的接口
与 外观模式 比较: 它们都缓存了一个复杂实体并自行对其进行初始化。 代理与其服务对象遵循同一接口, 使得自己和服务对象可以互换, 在这一点上它与外观不同。
示例代码部分
代理模式 实现 在 不同4s店(代理商) 或直销 汽车购买
# -*- coding: utf-8 -*-
"""
(C) Guangcai Ren
All rights reserved
create time '2020/11/8 17:00'
Usage:
代理模式 实现 在 不同4s店(代理商) 或直销 汽车购买
"""
from abc import ABC, abstractmethod
class CarSubject(ABC):
@abstractmethod
def buy_car(self, client_car_price):
pass
def remain_money(self, need_price, all_money):
"""
计算 买车后,剩余金额
:param need_price:
:param all_money:
:return:
"""
return f'剩余金额:{all_money - need_price}'
class ToyotaCar(CarSubject):
"""
没有中间商赚差价
"""
car_price = 100000
def buy_car(self, client_car_price):
if client_car_price < self.car_price:
print('用户金额不足,无法购买')
print(f'买丰田车成功,需要 {self.car_price} 元')
print(f'厂家扣除买车费用,{self.remain_money(self.car_price, client_car_price)}')
class ToyotaCarProxyA(CarSubject):
"""
丰田A代理商
额外需要 支付 汽车原价额外的 佣金比例
"""
commission_rate = 0.05 # 佣金比例
def __init__(self, toyotacar_obj):
"""
:param toyotacar:
"""
self.toyotacar_obj = toyotacar_obj
self.need_price = self.toyotacar_obj.car_price * (1 + self.commission_rate)
def buy_car(self, client_car_price):
"""
用户买车
:param client_car_price:
:return:
"""
self.check_price(client_car_price)
self.toyotacar_obj.buy_car(client_car_price)
print(self.remain_money(self.need_price, client_car_price))
def check_price(self, client_car_price):
"""
检查用户 买车金额 是否 够 佣金 汽车实际价格
:param client_car_price:
:return:
"""
if self.need_price > client_car_price:
raise Exception('用户金额不足以支持 佣金 和 汽车实际价格,无法购买')
class ToyotaCarProxyB(CarSubject):
"""
丰田B代理商
额外需要 支付 汽车原价额外的 佣金比例 和 手续费
"""
commission_rate = 0.02 # 佣金比例
service_charge = 200 # 服务费
def __init__(self, toyotacar_obj):
"""
:param toyotacar:
"""
self.toyotacar_obj = toyotacar_obj
self.need_price = self.toyotacar_obj.car_price * (1 + self.commission_rate) + self.service_charge
def buy_car(self, client_car_price):
"""
用户买车
:param client_car_price:
:return:
"""
self.check_price(client_car_price)
self.toyotacar_obj.buy_car(client_car_price)
print(self.remain_money(self.need_price, client_car_price))
def check_price(self, client_car_price):
"""
检查用户 买车金额 是否 够 佣金 汽车实际价格
:param client_car_price:
:return:
"""
if self.need_price > client_car_price:
raise Exception('用户金额不足以支持 佣金 和 汽车实际价格 和服务费,无法购买')
def client_buy_car(company_obj, client_car_price):
"""
客户买车
:param company_obj:
:param client_car_price:
:return:
"""
company_obj.buy_car(client_car_price)
if __name__ == '__main__':
# 没有中间商赚差价,直接 到丰田购买
print('直销 买车')
print(ToyotaCar.__doc__)
car_obj = ToyotaCar()
client_buy_car(car_obj, 120000)
print('*' * 10)
print('A经销商买车')
print(ToyotaCarProxyA.__doc__)
client_buy_car(ToyotaCarProxyA(car_obj), 120000)
print('*' * 10)
print('B经销商买车')
print(ToyotaCarProxyB.__doc__)
client_buy_car(ToyotaCarProxyB(car_obj), 120000)
运行结果:
总结:
代理模式 的 实际使用往小了说 实现一个代理类,在调用 某个服务接口(如某个类方法) 前 进行参数校验 也属于额外的服务; 往大了说 一个针对 多个业务系统 的 公共服务, 在对接每个 业务系统时,通过 代理模式实现, 每个 业务系统 通过 代理服务 访问 公共服务前 先判断 秘钥是否正确,参数是否合法等等前置操作,这样也减少了 公共服务的访问压力 等;
代理 模式 和 装饰模式 有些情况下非常相似(缓存代理,日志代理),甚至2种方式 均可实现,在对应的开发条件情况下,如果2种模式不相上下,这时 个人比较推荐 使用 装饰模式实现,因为代码结构不会进行较大改变,而且 装饰器毕竟用的比较多,简单易懂,后续人员维护等方便写;
相关链接:
https://refactoringguru.cn/design-patterns/proxy
https://refactoringguru.cn/design-patterns/proxy/python/example
http://c.biancheng.net/view/1359.html