在代理模式(Proxy Pattern)中,一個類代表另一個類的功能,這種型別的設計模式屬於結構型模式。
代理模式透過引入一個代理物件來控制對原物件的訪問。代理物件在客戶端和目標物件之間充當中介,負責將客戶端的請求轉發給目標物件,同時可以在轉發請求前後進行額外的處理。
在代理模式中,我們建立具有現有物件的物件,以便向外界提供功能介面。
介紹
意圖
為其他物件提供一種代理以控制對這個物件的訪問。
主要解決的問題
- 代理模式解決的是在直接訪問某些物件時可能遇到的問題,例如物件建立成本高、需要安全控制或遠端訪問等。
使用場景
- 當需要在訪問一個物件時進行一些控制或額外處理時。
實現方式
- 增加中間層:建立一個代理類,作為真實物件的中間層。
- 代理與真實物件組合:代理類持有真實物件的引用,並在訪問時進行控制。
關鍵程式碼
- 代理類:實現與真實物件相同的介面,並新增額外的控制邏輯。
- 真實物件:實際執行任務的物件。
應用例項
- 快捷方式:Windows系統中的快捷方式作為檔案或程式的代理。
- 角色扮演:孫悟空作為高翠蘭的代理,豬八戒無法區分。
- 代售點:購買火車票時,代售點作為火車站的代理。
- 支票:作為銀行賬戶資金的代理,控制資金的訪問。
- Spring AOP:使用代理模式來實現面向切面程式設計。
優點
- 職責分離:代理模式將訪問控制與業務邏輯分離。
- 擴充套件性:可以靈活地新增額外的功能或控制。
- 智慧化:可以智慧地處理訪問請求,如延遲載入、快取等。
缺點
- 效能開銷:增加了代理層可能會影響請求的處理速度。
- 實現複雜性:某些型別的代理模式實現起來可能較為複雜。
使用建議
- 根據具體需求選擇合適的代理型別,如遠端代理、虛擬代理、保護代理等。
- 確保代理類與真實物件介面一致,以便客戶端透明地使用代理。
注意事項
- 與介面卡模式的區別:介面卡模式改變介面,而代理模式不改變介面。
- 與裝飾器模式的區別:裝飾器模式用於增強功能,代理模式用於控制訪問。
結構
主要涉及到以下幾個核心角色:
-
抽象主題(Subject):
- 定義了真實主題和代理主題的共同介面,這樣在任何使用真實主題的地方都可以使用代理主題。
-
真實主題(Real Subject):
- 實現了抽象主題介面,是代理物件所代表的真實物件。客戶端直接訪問真實主題,但在某些情況下,可以透過代理主題來間接訪問。
-
代理(Proxy):
- 實現了抽象主題介面,並持有對真實主題的引用。代理主題通常在真實主題的基礎上提供一些額外的功能,例如延遲載入、許可權控制、日誌記錄等。
-
客戶端(Client):
- 使用抽象主題介面來操作真實主題或代理主題,不需要知道具體是哪一個實現類。
實現
我們將建立一個 Image 介面和實現了 Image 介面的實體類。ProxyImage 是一個代理類,減少 RealImage 物件載入的記憶體佔用。
ProxyPatternDemo 類使用 ProxyImage 來獲取要載入的 Image 物件,並按照需求進行顯示。
步驟 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