細說Python設計模式之代理模式

理解代理設計模式

  1. 代理通常就是一個介於尋求方和提供方之間的中介系統。尋求方是發出請求的一方,而提供方則是根據請求提供資源的一方。在Web世界中,它相當於代理服務器。客戶端(萬維網中的用戶)在向網站發出請求時,首先連接到代理服務器,然後向它請求諸如網頁之類的資源。代理服務器在內部評估此請求,將其發送到適當的服務器,當它收到響應後,就會將響應傳遞給客戶端。因此,代理服務器可以封裝請求、保護隱私,並且非常適合在分佈式架構中運行。
  2. 在設計模式的上下文中,代理是充當實際對象接口的類。對象類型可以是多樣化的,例如網絡連接、內存和文件中的大對象,等等。簡而言之,代理就是封裝實際服務對象的包裝器或代理人。代理可以爲其包裝的對象提供附加功能,而無需更改對象的代碼。代理模式的主要目的是爲其他對象提供一個代理者或佔位符,從而控制對實際對象的訪問。
代理模式可以用於多種場景:
  1. 它能夠以更簡單的方式表示一個複雜的系統。例如,涉及多個複雜計算或過程的系統應該提供一個更簡單的接口,讓它充 當客戶端的代理。
  2. 它提高了現有的實際對象的安全性。在許多情況下,都不允許客戶端直接訪問實際對象。這是因爲實際對象可能受到惡意活動的危害。這時候,代理就能起到抵禦惡意活動的盾牌作用,從而保護了實際的對象。
  3. 它爲不同服務器上的遠程對象提供本地接口。一個明顯的例子是客戶端希望在遠程系統上運行某些命令的分佈式系統,但客戶端可能沒有直接的權限來實現這一點。因此它將請求轉交給本地對象(代理),然後由遠程機器上的代理執行該請求。
  4. 它爲消耗大量內存的對象提供了一個輕量級的句柄。有時,你可能不想加載主要對象,除非它們真的有必要。這是因爲實際對象真的很笨重,可能需要消耗大量資源。一個典型的例子是網站用戶的個人簡介頭像。你最好在列表視圖中顯示簡介頭像的縮略圖,當然,爲了展示用戶簡介的詳細介紹,你就需要加載實際圖片了。
    讓我們通過一個簡單的例子來理解該模式。不妨以演員與他的經紀人爲例,當製作公司想要找演員拍電影時,他們通常會與經紀人交流,而不是直接跟演員交流。經紀人會根據演員的日程安排和其他合約情況,來答覆製作公司該演員是否有空,以及是否對該影片感興趣。在這種情況下,製作公司並不直接找演員交涉,而是通過經紀人作爲代理,處理所有與演員有關的調度和片酬問題。

示例: 其中Actor是代理。對象Agent用於查看Actor是否正處於忙碌狀態。如果Actor正忙,則調用Actor().occupied()方法;如果Actor不忙,則返回Actor().available()方法。

# -*- coding: utf-8 -*-
# @Time    : 2020/6/6 上午 11:13
# @Author  : lh
# @Email   : [email protected]
# @File    : test.py
# @Software: PyCharm


class Actor(object):
    def __init__(self):
        self.isBusy = False

    def occupied(self):
        self.isBusy = True
        print(type(self).__name__, "忙於當前的電影")

    def available(self):
        self.isBusy = False
        print(type(self).__name__, "是處於空閒的狀態的")

    def getStatus(self):
        return self.isBusy


class Agent(object):
    def __init__(self):
        self.principal = None
    def work(self):
        self.actor = Actor()
        if self.actor.getStatus():
            self.actor.occupied()
        else:
            self.actor.available()

if __name__ == "__main__":
    a = Agent()
    a.work()
代理設計模式的功能:
  • 它爲其他對象提供了一個代理,從而實現原始對象的控制訪問
  • 它可以作用於一個層或接口,以支持分佈式訪問。
  • 它通過增加代理,保護真正的組件不受意外的影響。

代理模式的UML圖:

代理模式有3個主要角色:製作公司、經紀人和演員。下面,讓我們把這些角色放入一個UML圖中,看看這些類如何關聯:
在這裏插入圖片描述

  • 代理:它維護一個引用,允許代理(Proxy)通過這個引用來訪問實際對象。它提供了一個與主題(Subject)相同的接口,以便代理可以替換真實的主題。代理還負責創建和刪除真實主題。
  • 它定義了RealSbuject和Proxy的公共接口。以Proxy和RealSubject的形式實現主題(Subject),使用RealSubject的任何地方都可以使用代理(Proxy)。
  • 真實主題:它定義代理(Proxy)所代表的真實對象。

數據結構角度來看問題,表示:

  • 代理:它是一個控制對RealSubject類訪問的類,它處理客戶端的請求,負責創建或刪除RealSubject。
    主題/真實主題:主題是定義真實主題(RealSubject)和代理(Proxy)相類似的接口。RealSubject是Subject接口的實際實現。它提供了真正的功能,然後又客戶端使用。
  • 客戶端: 它訪問要完成工作的Proxy類。Proxy類在內部控制RealSubject的訪問,並引導客戶端(Client)所請求的工作。
瞭解不同類型的代理
  • 虛擬代理
  • 遠程代理
  • 保護代理
  • 智能代理

1. 虛擬代理

  • 如果一個對象實例化後會佔用大量內存的話,可以先利用佔位符來表示,這就是所謂的虛擬代理。例如,假設你想在網站上加載大型圖片,而這個請求需要很長時間才能加載完成。通常,開發人員將在網頁上創建一個佔位符圖標,以提示這裏有圖像。但是,只有當用戶實際點擊圖標時纔會加載圖像,從而節省了向存儲器中加載大型圖像的開銷。因此,在虛擬代理中,當客戶端請求或訪問對象時,纔會創建實際對象。
    2. 遠程代理
  • 遠程代理可表述如下:它給位於遠程服務器或不同地址空間上的實際對象提供了一個本地表示。例如,你希望爲應用程序建立一個監控系統,而該應用涉及多個Web服務器、數據庫服務器、芹菜(celery)任務服務器、緩存服務器,等等。如果我們要監視這些服務器的CPU和磁盤利用率,就需要建立一個對象,該對象能夠用於監視應用程序運行的上下文中,同時還可以執行遠程命令以獲取實際的參數值。在這種情況下,建立一個作爲遠程對象的本地表示的遠程代理對象將可以幫助我們實現這個目標。

3. 保護代理

  • 你可以通過以下幾點加深對保護代理的理解。這種代理能夠控制RealSubject的敏感對象的訪問。例如,在當今分佈式系統的世界中,Web應用會提供多個服務,這些服務相互協作來提供各種功能。現在,在這樣的系統中,認證服務充當負責認證和授權的保護性代理服務器。在這種情況下,代理自然有助於保護網站的核心功能,防止無法識別或未授權的代理訪問它們。因此,代理對象會檢查調用者是否具有轉發請求所需的訪問權限。

4. 智能代理

  • 智能代理在訪問對象時插入其他操作。例如,假設在系統中有一個核心組件,它將狀態信息集中保存在一個地點。通常情況下,這樣的組件需要被多個不同的服務調用以完成它們的任務,並且可能導致共享資源的問題。與讓服務直接調用核心組件不同,智能代理是內置的,並且會在訪問之前檢查實際對象是否被鎖定,以確保沒有其他對象可以更改它。
現實世界中的代理模式

讓我們假設,你在商場溜達,看中了一件漂亮的牛仔衫。你想買這件襯衫,但手裏的現金卻不夠了。 這要是在不久以前,你可以去ATM取錢,然後來到商場付款。在更早的時候,通常使用的是銀行支票,這樣你就必須去銀行提款,然後再回商場付款。 得益於銀行業務的發展,後來出現了一種稱爲借記卡的東西。所以現在,你買東西的時候,只要在商家刷一下借記卡,這筆錢就會劃入商家的賬戶,從而完成支付。 下面,我們利用Python v3.5來開發一個應用程序,實現上述示例。首先從客戶端開始:你去了商場,現在想買一件漂亮的牛仔衫。讓我們看看如何編寫客戶端代碼。

  • 你的行爲由類You(即客戶端)來表示。
    爲了購買襯衫,該類提供了make_payment()方法。
  • 特殊方法__init __()會調用代理並將其實例化。
  • make_payment()方法在內部調用代理的方法進行付款。
  • 如果付款成功,將返回__del __()方法。
class Your:
    def __init__(self):
        print("我們買那件牛仔襯衫吧")
        self.debitCard = DebitCard() # DebitCard
        self.isPurchased = None

    def make_payment(self):
        self.isPurchased = self.debitCard.do_play()

    def __del__(self):
        if self.isPurchased:
            print("你:哇!牛仔襯衫是我的")
        else:
            print("我應該多掙些錢")


you = Your()
you.make_payment()
  • 主題是由代理和真實主題實現的接口。
    Bank實際完成從你的賬戶向商家賬戶劃賬的工作。
  • Bank提供了多個方法來處理付款。代理使用setCard()方法將借記卡詳細信息發送給銀行。
  • __getAccount()方法是Bank的私有方法,用於獲取借記卡持有人的賬戶詳細信息。爲了簡單起見,我們強制使用與賬號相同的借記卡號。
  • Bank還有__hasFunds()方法,它用來查看賬戶持有人在賬戶中是否有足夠的資金來爲襯衫付款。
  • 由Bank類(通過Payment接口)實現的do_pay()方法實際上負責根據可用資金向商家付款:
  • 讓我們現在來理解最後一部分,即與代理有關的部分。
  • DebitCard類是此處的代理。當你想要付款時,它會調用do_pay()方法。這是因爲你不想跑去銀行提款,然後再跑回商家完成支付。
  • DebitCard類充當真實主題(銀行)的代理。payWithCard()方法在內部控制真實主題(Bank類)對象的創建,並向銀行提供借記卡的詳細信息。Bank在內部對賬戶進行檢查並完成支付
代理模式的優點:
  1. 代理模式可以通過緩存笨重的對象或頻繁訪問的對象來提高應用程序的性能。
  2. 代理模式還提供對於真實主題的訪問授權。因此,只有提供合適權限的情況下,這個模式纔會接受委派。
  3. 遠程代理還便於與可用做網絡連接和數據庫連接的遠程服務器進行交互,並可用於監視系統。
門面模式和代理模式之間的比較

門面模式和代理模式都是結構型設計模式。它們的相似之處在於,都是在真實對象的前面加入一個代理/門面對象。但是在意圖或目的方面,這兩種模式的確存在差異,如表:

代理模式 門面模式
它爲其它對象提供了代理和佔位符,以控制對原始對象的訪問 它爲類的大型子系統提供了一個接口。
代理對象具有與目標對象相同的接口,並保存有目標對象的引用 它實現了子系統之間的通信和依賴性的最小化。
它充當客戶端和被封裝的對象之間的中介。 門面對象提供了單一的簡單接口。
解惑:

問: 裝飾器模式和代理模式之間有什麼區別?
**答:**裝飾器向運行時裝飾的對象添加行爲,而代理則是控制對對象的訪問。代理和真實主題之間的關聯是在編譯時完成的,而不是動態的。

**問:**代理模式的缺點是什麼?
**答:**代理模式會增加響應時間。例如,如果代理沒有良好的體系結構或存在性能問題,它就會增加真實主題的響應時間。一般來說,這一切都卻取決於代理寫的有多好。

**問:**客戶端可以獨立訪問真實主題嗎?
**答:**是的,但是代理模式能夠提供許多優勢,例如虛擬、遠程等,所以使用代理模式會更好一點。

**問:**代理模式是否能夠給自己添加任何功能?
**答:**代理可以向真實主題添加額外的功能,而無需更改對象的代碼。代理和真實主題可以實現相同的接口。

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