代理模式(Proxy Design Pattern)用于在不改变原始类(被代理类)代码的情况下,通过引入代理给原始类提供附加功能。
代理就是真实对象的代表,不对外暴露真实对象。
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式的优缺点
优点
- 职责清晰:真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
- 代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。
- 高扩展性。
代理的分类
- 静态代理
- 动态代理
应用场景
- 非功能或业务性的需求开发:监控、统计、鉴权、限流、事务、日志等。
- 通过一个中介控制,使实际对象的安全性更高。
- 为不同服务器上的远程对象提供本地的接口,比如RPC。
- 消耗大量内存的对象提供一个轻量级的句柄。
静态代理
代理模式的构成
代理模式主要由三部分构成:
- 原始类(被代理类)
- 抽象类(实现跟原始类一样的接口)
- 代理类(实现抽象类)
使用方式
静态代理主要用于内部系统的开发,静态代理的前提,那就是真实类和代理类要实现同一个接口,静态代理主要的步骤可以总结为:
- 代理类和原始类实现同一抽象类接口。
- 代理类中添加附加的功能,不影响原始类代码。
- 将原始类使用的地方替换为代理类。
我们通过代码的方式实现一下,我们以生活中通过中介租房为案例。
通常情况下,我们要租房,但是因为没有渠道,找不到好的房源或者担心私下交易的安全性问题;而房子出租方也是同样,因为没有渠道,找不到好的租客以及担心私下交易的安全性,这时候通过一个合法的中介来充当媒介就是非常好的办法。
抽象类
房东(原始类)和代理类(中介)都要实现 rent_house 的方法。
class IRentHouse(ABC):
@abstractmethod
def rent_house(self):
"""
出租房屋
"""
pass
房东(原始类)
class RentHouse(IRentHouse):
def rent_house(self):
print("我是房东,要出租房子")
中介(代理类)
在实现租房功能的基础上,增加了检查房子出租合法性,以及收取中介费的附加功能,并不影响原来房东出租房子的方法。
class Proxy(IRentHouse):
def __init__(self, rent_obj: RentHouse):
self.real_object = rent_obj
def check_house(self):
print("检查房子是否合法")
def get_pay(self):
print("获取中介费")
def rent_house(self):
"""
实现租房功能的基础上,提供附加功能
"""
# 检查房子是否合法
self.check_house()
# 实现租房功能
self.real_object.rent_house()
# 收取中介费
self.get_pay()
调用
# 直接租房者与房东沟通,可能存在风险
RentHouse().rent_house()
# 通过中介,安全实现租房者租房,房东出租房
rent_obj = RentHouse()
Proxy(rent_obj).rent_house()
动态代理
从静态代理的实现中可以发现,静态代理的缺点显而易见,当真实类的方法越来越多的时候,这样构建的代理类的代码量是非常大的,所以就引进动态代理。
动态代理原理
代理类想要动态的实现知道原始类的所有属性和方法,在 java 中,这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。
而 python 没有直接提供有动态代理的设计模型,但我们可以使用 python 中的特殊方法来实现。
使用方法
通过一个公共代理类包装真实类,请求通过代理类实现,从而实现想要增强的功能。
公共代理类
class DynamicProxy:
def __init__(self, target):
self.target = target # 传入的真实类
def __getattr__(self, item):
print("进来了")
raise ModuleNotFoundError("{}方法或属性不存在".format(item))
def __getattribute__(self, name):
target = object.__getattribute__(self, "target")
attr = object.__getattribute__(target, name)
def newAttr(*args, **kwargs): # 包装
print("前操作")
res = attr(*args, **kwargs)
print("后操作")
return res
return newAttr
-
target 是传入的真实类,也就是要被代理的类。
-
__getattribute__
是 python 中的特殊方法,获取属性和方法时,会触发__getattribute()__方法。 -
__getattr__
是 python 中的另一个特殊方法,当__getattribute__
获取不到属性或方法时,就会触发 getattr 进行兜底。
通过两个特殊方法的配合,就可以实现将传入的真实类绑定在代理上,并获取真实类是否存在某方法或属性从而实现动态代理。
代码实例:
# 真实类
class RentHouse:
def rent_house(self, a):
print(a)
print("我是房东,要出租房子")
def check_house(self):
print("检查一下房子")
target = RentHouse()
proxy = DynamicProxy(target)
proxy.rent_house(666)
proxy.rent_house2(666)
# 执行结果
前操作
666
我是房东,要出租房子
后操作
进来了
ModuleNotFoundError: rent_house2方法或属性不存在
总结
代理模式可以在不改动原始类的前提上,附加一些非业务性的功能,比如:监控、统计、鉴权、限流、事务、日志等等,可以将这些功能与业务逻辑解耦,让业务只关注业务,是非常好用的一种结构型设计模式。