代理模式

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完成支付

5.4 輸出結果

在這裏插入圖片描述
github地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章