用实例理解设计模式——代理模式(Python版)
代理模式
:为其他对象提供一种代理以控制对这个对象的访问。
在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式分为:
- 静态代理
- 动态代理
由下面三部分组成:
抽象角色
:通过接口或抽象类声明真实角色实现的业务方法。
代理角色
:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
真实角色
:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
静态代理
在程序运行前,就已经确定代理类和委托类的关系的代理方式,被称为静态代理
。
例:小明请律师进行诉讼
诉讼流程可抽象为ILawsuit
类,如下:
import abc class ILawsuit(metaclass=abc.ABCMeta): @abc.abstractmethod def submit(self): # 提交申请 pass @abc.abstractmethod def burden(self): # 进行举证 pass @abc.abstractmethod def defend(self): # 开始辩护 pass @abc.abstractmethod def finish(self): # 诉讼完成 pass
小明为具体诉讼人,可写为Litigant
类,如下:
class Litigant(ILawsuit): # 继承于ILawsuit def __init__(self, name): self.name = name def submit(self): print(f'{self.name}申请仲裁!') def burden(self): print('证据如下:XXXXXX') def defend(self): print('辩护过程:XXXXXX') def finish(self): print('诉讼结果如下:XXXXXX')
律师可写为Lawyer
类,如下:
class Lawyer(ILawsuit): # 继承于ILawsuit def __init__(self, litigant): self.litigant = litigant # 具体诉讼人 def submit(self): self.litigant.submit() def burden(self): self.litigant.burden() def defend(self): self.litigant.defend() def finish(self): self.litigant.finish()
诉讼过程,可表示为:
if __name__ == '__main__': xiaoming = Litigant('小明') lawyer = Lawyer(xiaoming) lawyer.submit() # 律师提交诉讼申请 lawyer.burden() # 律师进行举证 lawyer.defend() # 律师替小明辩护 lawyer.finish() # 完成诉讼 # 输出结果 小明申请仲裁! 证据如下:XXXXXX 辩护过程:XXXXXX 诉讼结果如下:XXXXXX
静态代理的优缺点
优点
:业务类只需要关注业务逻辑本身,保证了业务类的重用性。
缺点
:代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
动态代理
代理类在程序运行时创建的代理方式被称为 动态代理
。
也就是说,这种情况下,代理类并不是在代码中定义的,而是在运行时根据我们在代码中的指示
动态生成的。
同样,我们举例说明:
通常我们调用REST API通常可能是这样的:
import urllib import json def fetch_resource(resource_id): opener = urllib.urlopen('http://remote.server/api/resource/' + resource_id) if opener.code != 200: raise RuntimeError('invalid return code!') content = opener.read() try: return json.loads(content) except ValueError: return content
对于每一个REST操作,都会有类似的代码。差别仅在于API的地址和HTTP method(GET、POST、等)。此时,可以引入一个GetProxy
,可以代替我们实现这些繁杂的工作。
import urllib import json class GetProxy(object): def __getattr__(self, api_path): def _rest_fetch(*paras): opener = urllib.urlopen('http://remote.server/api/' + api_path + '/' + '/'.join(resource_id)) if opener.code != 200: raise RuntimeError('invalid return code!') content = opener.read() try: return json.loads(content) except ValueError: return content return _rest_fetch
此时,新的调用方式如下:
proxy = GetProxy() # 调用API proxy.user(123) # http://remote.server/api/user/123 proxy.resource('switch', 456) # http://remote.server/api/resource/switch/456
可见,通过动态代理,极大简化了调用过程。
相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
参考
http://adolph.cc/15712984956484.html
https://blog.zhangyu.so/python/2016/02/24/design-patterns-of-python-proxy/