用實例理解設計模式——代理模式(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/
在這里插入圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM