代理模式(Proxy Design Pattern)及代码实现

什么是 代理模式?

代理模式 通过名字 得知, 是提供 一个第三方 服务 给 客户端使用,这个第三方服务 对接 真实服务,并且 实现了 真实服务 功能,并且还增加了 额外的功能; 

 

生活中的例子

  • 12306代理购票(黄牛软件); 此类软件 不仅实现了 购票的功能,而且还增加 了 抢票功能,额外 手续费 功能 等等;
  • 4s店 买车; 这种模式 不仅 实现了 选车,试驾,购车服务, 还增加了 额外的 强制性消费 如 手续费,贴膜,上保险 等 恶心服务;
  • 双方交易,其中 一方 不想透露身份,通过 委托某中间人 进行代理交易; 这样 起到了  保护 目标对象 的作用;

 

代码中的例子:

  • 在业务系统中,可能需要 调用 数据服务 查询数据,这时 如果每次入参都相同,还是查询数据 会造成 数据服务的压力,这时 可以通过 代理模式 实现 缓存代理服务,此服务 先查询自己服务是否有缓存,没有再 将请求转发到 数据服务;

 

识别此模式方法:

  • 代理模式会将所有实际工作委派给一些其他对象。 除非代理是某个服务的子类, 否则每个代理方法最后都应该引用一个服务对象。

 

该模式使用场景:

  • 远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。
  • 虚拟代理,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
  • 安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。
  • 智能指引,主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它。
  • 延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,Hibernate 中就存在属性的延迟加载和关联表的延时加载。
  • 日志记录/缓存 代理,  这种单纯的 增加额外功能的 代理方式,可以通过 装饰器模式实现(即使对方是第三方sdk包); 具体使用哪种方式 根据实际情况 和个人喜好 自行选择;

 

该模式关键的角色:

  1. 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  2. 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
  3. 代理(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

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值