1 代理模式
代理模式
爲另一個對象提供一個替身或佔位符,並控制
對這個對象的訪問
。
2 代理模式的UML類圖
Subject
:爲RealSubject和Proxy提供接口,通過實現這個接口,Proxy在RealSubject出現的地方取代它。RealSubject
:是真正做事的對象,它是被Proxy代理和控制訪問的對象。Proxy
:持有對RealSubject的引用。在某些例子中,Proxy還負責RealSubject對象的創建與銷燬。客戶與RealSubject的交互必須通過Proxy。因爲Proxy和RealSubject實現的相同的接口Subject,因此任何用到RealSubject的地方,都可以使用Proxy取代。某些情況下,需要這些控制。這些情況包括RealSubject是遠程對象、RealSubject創建開銷大或 RealSubject需要被保護。
3 代理模式的類型
代理模式創建一個代表對象,讓代表對象控制某對象的訪問。
3.1 遠程代理
遠程代理控制遠程對象的訪問
。
Proxy和RealSubject需要通過網絡遠程訪問。在Java中,可以使用RMI(遠程方法調用)技術來實現。
例子:糖果監視器GumballMonitor監視糖果機GumballMachine的狀態,其中糖果機GumballMachine位於遠程的另一個JVM中,糖果監視器GumballMonitor通過ProxyProxy控制訪問遠程對象 。
3.2 虛擬代理
虛擬代理控制訪問
那些創建開銷大的資源
。
例子:CD封面的顯示。
ImageProxy工作流程
- 1 ImageProxy首先創建一個ImageIcon對象,然後從網上URL上加載圖像。
- 2 在加載過程中,ImageProxy顯示“CD封面正在加載,請等候…”。
- 3 當圖像加載完畢,ImageProxy把所有方法調用委託給ImageIcon,這些方法包括了paintIcon()、getWidth()和getHeight()。
- 4 當用戶請求新的圖像時,創建新的代理,重複這樣的過程。
3.3 保護代理
保護代理基於權限控制對資源的訪問
。
例子:男女配對的約會服務系統。每個用戶不能改變自己的評分,也不可以改變其他用戶的個人信息。因此,需要兩個代理,一個控制訪問自己的對象,另一個控制訪問其他用戶的對象。這必須使用Java API的動態代理。
3.4 防火牆代理
控制網絡資源的訪問,保護主體免於“壞客戶”的侵害。
3.5 緩存代理
爲開銷大的運算結果提供暫時存儲:它允許多個客戶共享結果,以減少計算或網絡延遲。
3.6 同步代理
在多線程的情況下爲主題提供安全的訪問。
3.7 複雜隱藏代理
用來隱藏一個類的複雜集合的複雜度,並進行訪問控制,有時候也稱爲外觀代理(Facade Proxy)。複雜隱藏代理與外觀模式(Facade)不一樣,因爲代理控制訪問,而外觀模式爲複雜子系統提供簡潔的接口。
3.8 寫入複製代理(Copy-On-Write Proxy)
用來控制對象的複製,方法是延遲對象的複製,直到客戶真的需要爲止。是虛擬代理的一種變體。
3.9 智能引用代理
當主題被引用時,進行額外的動作。例如計算一個對象被引用的次數。
4 代理模式與裝飾者模式的區別
代理模式 | 裝飾者模式 |
---|---|
在編譯期控制對象的訪問 | 在運行時動態爲對象添加行爲 |
5 虛擬代理的一個Python實現例子
5.1 例子解釋
假設你在商場買衣服,當你要支付的時候,由於現金不足,你現在需要使用信用卡來支付。這裏,信用卡充當了一個代理Proxy,實際的主題類RealSubject爲銀行,當你用信用卡支付時,實際上是從銀行轉賬到商家賬號。
5.2 UML類圖
5.3 代碼實現
- 客戶端You類
- 首先構造函數
__init__()
會調用代理類,並實例化一個代理對象賦值給debitCard屬性
- 當你購買衣服時,需要調用
make_payment()
方法,在該方法內部實際上調用代理的do_pay()方法進行支付
- 如果付款成功,將返回__del__()方法,該方法內部
需要根據屬性isPurchased來判斷是否購買成功
- 首先構造函數
class You:
'''
客戶端類
'''
def __init__(self):
print("You:: Let's buy Denim shirt!")
self.debitCard = DebitCard()
self.isPurchased = None
def make_payment(self):
self.isPurchased = self.debitCard.do_pay()
def __del__(self):
'''
析構函數
'''
if self.isPurchased:
print("You:: Wow! Denim shirt is Mine :-)")
else:
print("You:: I should earn more money :-(")
#if __name__=='__main__':
you = You()
you.make_payment()
- 主題接口Payment類
抽象基類,具有一個do_pay()方法,需要藉助代理類和真實主題類來實現付款
from abc import ABCMeta, abstractmethod
class Payment(metaclass=ABCMeta):
'''
主題類,抽象基類接口
'''
@abstractmethod
def do_pay(self):
pass
- 真實主題Bank類
- Bank實際完成從你的信用卡轉賬到商家的賬號的工作
- 要完成付款操作,需要多個方法來處理。首先需要代理使用
setCard(card)方法將信用卡卡號發送給銀行
-__getAccount()方法
是Bank的私有方法,用於獲得信用卡持有人的賬戶的詳細信息
。爲了簡單,將賬號信息account設置爲信用卡卡號card,並返回賬號信息account
__hasFunds()
方法,用於查看信用卡持有人是否還擁有足夠的資金來付款
do_pay()
方法先判斷是否有足夠資金
,若有,則完成用可用資金先商家付款的操作,否則打印資金不足
class Bank(Payment):
'''
實際主題類
'''
def __init__(self):
self.card = None
self.account = None
def __getAccount(self):
self.account = self.card # 爲了簡單,使用卡號作爲賬號信息
return self.account
def __hasFunds(self):
print("Bank:: Checking if Account", self.__getAccount(), "has enough funds")
return True
def setCard(self, card):
self.card = card
def do_pay(self):
if self.__hasFunds():
print("Bank:: Paying the merchant")
return True
else:
print("Bank:: Sorry, not enough funds!")
return False
- 代理DebitCard類
__init__()
構造函數實例化一個真實主題類Bank對象bank
do_pay()
方法請求輸入卡號
,並調用真實主題Bank的setCard(card)方法將卡號信息發送給Bank
,然後調用Bank的do_pay()方法完成支付
class DebitCard(Payment):
'''
代理類Proxy
'''
def __init__(self):
self.bank = Bank()
def do_pay(self):
card = input("Proxy:: Punch in card number: ") # 輸入卡號信息
self.bank.setCard(card) # 使用卡號信息初始化bank賬號
return self.bank.do_pay() #調用實際主題類對象bank的do_pay完成支付