用实例理解设计模式——代理模式(Python版)

paopao00 2020-01-07

代理模式为其他对象提供一种代理以控制对这个对象的访问。

在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

代理模式分为:

  • 静态代理
  • 动态代理

由下面三部分组成

抽象角色:通过接口或抽象类声明真实角色实现的业务方法。

代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

静态代理

在程序运行前,就已经确定代理类和委托类的关系的代理方式,被称为静态代理

例:小明请律师进行诉讼

诉讼流程可抽象为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/
用实例理解设计模式——代理模式(Python版)

相关推荐