代理模式
:為其他對象提供一種代理以控制對這個對象的訪問。
在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。
代理模式分為:
- 靜態代理
- 動態代理
由下面三部分組成:
抽象角色
:通過接口或抽象類聲明真實角色實現的業務方法。
代理角色
:實現抽象角色,是真實角色的代理,通過真實角色的業務邏輯方法來實現抽象方法,並可以附加自己的操作。
真實角色
:實現抽象角色,定義真實角色所要實現的業務邏輯,供代理角色調用。
靜態代理
在程序運行前,就已經確定代理類和委托類的關系的代理方式,被稱為靜態代理
。
例:小明請律師進行訴訟
訴訟流程可抽象為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/