代理模式

dengyantao發表於2024-08-19

在代理模式(Proxy Pattern)中,一個類代表另一個類的功能,這種型別的設計模式屬於結構型模式。

代理模式透過引入一個代理物件來控制對原物件的訪問。代理物件在客戶端和目標物件之間充當中介,負責將客戶端的請求轉發給目標物件,同時可以在轉發請求前後進行額外的處理。

在代理模式中,我們建立具有現有物件的物件,以便向外界提供功能介面。

介紹

意圖

為其他物件提供一種代理以控制對這個物件的訪問。

主要解決的問題

  • 代理模式解決的是在直接訪問某些物件時可能遇到的問題,例如物件建立成本高、需要安全控制或遠端訪問等。

使用場景

  • 當需要在訪問一個物件時進行一些控制或額外處理時。

實現方式

  • 增加中間層:建立一個代理類,作為真實物件的中間層。
  • 代理與真實物件組合:代理類持有真實物件的引用,並在訪問時進行控制。

關鍵程式碼

  • 代理類:實現與真實物件相同的介面,並新增額外的控制邏輯。
  • 真實物件:實際執行任務的物件。

應用例項

  • 快捷方式:Windows系統中的快捷方式作為檔案或程式的代理。
  • 角色扮演:孫悟空作為高翠蘭的代理,豬八戒無法區分。
  • 代售點:購買火車票時,代售點作為火車站的代理。
  • 支票:作為銀行賬戶資金的代理,控制資金的訪問。
  • Spring AOP:使用代理模式來實現面向切面程式設計。

優點

  • 職責分離:代理模式將訪問控制與業務邏輯分離。
  • 擴充套件性:可以靈活地新增額外的功能或控制。
  • 智慧化:可以智慧地處理訪問請求,如延遲載入、快取等。

缺點

  • 效能開銷:增加了代理層可能會影響請求的處理速度。
  • 實現複雜性:某些型別的代理模式實現起來可能較為複雜。

使用建議

  • 根據具體需求選擇合適的代理型別,如遠端代理、虛擬代理、保護代理等。
  • 確保代理類與真實物件介面一致,以便客戶端透明地使用代理。

注意事項

  • 與介面卡模式的區別:介面卡模式改變介面,而代理模式不改變介面。
  • 與裝飾器模式的區別:裝飾器模式用於增強功能,代理模式用於控制訪問。

結構

主要涉及到以下幾個核心角色:

  • 抽象主題(Subject):

    • 定義了真實主題和代理主題的共同介面,這樣在任何使用真實主題的地方都可以使用代理主題。
  • 真實主題(Real Subject):

    • 實現了抽象主題介面,是代理物件所代表的真實物件。客戶端直接訪問真實主題,但在某些情況下,可以透過代理主題來間接訪問。
  • 代理(Proxy):

    • 實現了抽象主題介面,並持有對真實主題的引用。代理主題通常在真實主題的基礎上提供一些額外的功能,例如延遲載入、許可權控制、日誌記錄等。
  • 客戶端(Client):

    • 使用抽象主題介面來操作真實主題或代理主題,不需要知道具體是哪一個實現類。

實現

我們將建立一個 Image 介面和實現了 Image 介面的實體類。ProxyImage 是一個代理類,減少 RealImage 物件載入的記憶體佔用。

ProxyPatternDemo 類使用 ProxyImage 來獲取要載入的 Image 物件,並按照需求進行顯示。

代理模式的 UML 圖

步驟 1

建立一個介面。

Image.java

public interface Image { void display(); }

步驟 2

建立實現介面的實體類。

RealImage.java

public class RealImage implements Image { private String fileName; public RealImage(String fileName){ this.fileName = fileName; loadFromDisk(fileName); } @Override public void display() { System.out.println("Displaying " + fileName); } private void loadFromDisk(String fileName){ System.out.println("Loading " + fileName); } }

ProxyImage.java

public class ProxyImage implements Image{ private RealImage realImage; private String fileName; public ProxyImage(String fileName){ this.fileName = fileName; } @Override public void display() { if(realImage == null){ realImage = new RealImage(fileName); } realImage.display(); } }

步驟 3

當被請求時,使用 ProxyImage 來獲取 RealImage 類的物件。

ProxyPatternDemo.java

public class ProxyPatternDemo { public static void main(String[] args) { Image image = new ProxyImage("test_10mb.jpg"); // 影像將從磁碟載入 image.display(); System.out.println(""); // 影像不需要從磁碟載入 image.display(); } }

步驟 4

執行程式,輸出結果:

Loading test_10mb.jpg
Displaying test_10mb.jpg

Displaying test_10mb.jpg

相關文章