簡介
代理模式是一種結構型設計模式,它可以讓我們透過一個代理物件來訪問一個真實的目標物件,從而實現對目標物件的功能擴充套件或保護。代理模式的主要角色有三個:
- 抽象主題(Subject):定義了真實主題和代理主題的公共介面,使得在任何使用真實主題的地方都可以使用代理主題。
- 真實主題(RealSubject):實現了抽象主題的介面,定義了真實的業務邏輯,是代理主題所代表的真實物件。
- 代理主題(Proxy):也實現了抽象主題的介面,但是在呼叫真實主題的方法之前或之後,可以執行一些額外的操作,從而對真實主題進行控制或增強。
代理模式可以幫助我們解決以下幾種問題:
- 當我們無法或不想直接訪問一個物件時,可以透過一個代理物件來間接訪問,例如遠端代理、虛擬代理等。
- 當我們想要給一個物件提供額外的功能時,可以透過一個代理物件來實現,而不需要修改原有的物件,例如快取代理、日誌代理等。
- 當我們想要給一個物件增加一些訪問控制或安全保護時,可以透過一個代理物件來實現,例如防火牆代理、許可權代理等。
實現
根據代理模式的定義,我們可以用以下的類圖來表示它的結構:
其中,Client是客戶端類,它需要使用Subject介面提供的方法。Proxy是代理類,它持有一個RealSubject的引用,並且實現了Subject介面。RealSubject是真實類,它也實現了Subject介面,並且定義了具體的業務邏輯。
代理模式有多種型別,例如靜態代理、動態代理等,代理模式也有自己的優缺點,使用時需要根據具體的場景和需求來選擇合適的型別和方式。
靜態代理實現
下面我們用Java程式碼來實現一個靜態代理的例子:
// 抽象主題介面
public interface Subject {
// 定義一個抽象方法
void request();
}
// 真實主題類
public class RealSubject implements Subject {
// 實現抽象方法
@Override
public void request() {
// 真實的業務邏輯
System.out.println("RealSubject is doing something...");
}
}
// 代理主題類
public class Proxy implements Subject {
// 持有一個真實主題的引用
private RealSubject realSubject;
// 構造方法,傳入一個真實主題物件
public Proxy(RealSubject realSubject) {
this.realSubject = realSubject;
}
// 實現抽象方法
@Override
public void request() {
// 在呼叫真實主題之前,可以執行一些額外操作
System.out.println("Proxy is doing something before...");
// 呼叫真實主題的方法
realSubject.request();
// 在呼叫真實主題之後,可以執行一些額外操作
System.out.println("Proxy is doing something after...");
}
}
// 客戶端類
public class Client {
public static void main(String[] args) {
// 建立一個真實主題物件
RealSubject realSubject = new RealSubject();
// 建立一個代理物件,並傳入真實主題物件
Proxy proxy = new Proxy(realSubject);
// 使用代理物件來呼叫抽象方法
proxy.request();
}
}
執行結果如下:
Proxy is doing something before...
RealSubject is doing something...
Proxy is doing something after...
從執行結果可以看出,代理物件在呼叫真實物件的方法之前和之後,都執行了一些額外的操作,從而對真實物件進行了增強或控制。
動態代理實現
動態代理是一種特殊的代理模式,它可以在執行時動態地建立代理物件,而不需要事先定義代理類。動態代理可以更靈活地適應不同的場景和需求,但是也更復雜和難以理解。
這個例子是使用JDK動態代理來實現一個日誌代理,它可以在呼叫目標物件的方法之前和之後,記錄相關的日誌資訊。程式碼如下:
// 抽象主題介面
public interface Subject {
// 定義一個抽象方法
void request();
}
// 真實主題類
public class RealSubject implements Subject {
// 實現抽象方法
@Override
public void request() {
// 真實的業務邏輯
System.out.println("RealSubject is doing something...");
}
}
// 日誌處理器類,實現了InvocationHandler介面,用於定義代理邏輯
public class LogHandler implements InvocationHandler {
// 持有一個目標物件的引用
private Object target;
// 構造方法,傳入一個目標物件
public LogHandler(Object target) {
this.target = target;
}
// 實現invoke方法,用於呼叫目標物件的方法,並在之前和之後執行日誌操作
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在呼叫目標物件之前,記錄開始時間
long startTime = System.currentTimeMillis();
System.out.println("開始執行" + method.getName() + "方法...");
// 呼叫目標物件的方法,並獲取返回值
Object result = method.invoke(target, args);
// 在呼叫目標物件之後,記錄結束時間和耗時
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
System.out.println("結束執行" + method.getName() + "方法,耗時" + duration + "毫秒");
// 返回結果
return result;
}
}
// 客戶端類
public class Client {
public static void main(String[] args) {
// 建立一個真實主題物件
RealSubject realSubject = new RealSubject();
// 建立一個日誌處理器物件,並傳入真實主題物件
LogHandler logHandler = new LogHandler(realSubject);
// 使用Proxy類的靜態方法newProxyInstance來動態地建立一個代理物件,傳入真實主題物件的類載入器、介面和處理器
Subject proxy = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), logHandler);
// 使用代理物件來呼叫抽象方法
proxy.request();
}
}
執行結果如下:
開始執行request方法...
RealSubject is doing something...
結束執行request方法,耗時1毫秒
從執行結果可以看出,代理物件在呼叫真實物件的方法之前和之後,都執行了一些日誌操作,從而對真實物件進行了增強。
優缺點
靜態代理模式
優點:
- 代理模式可以實現對真實物件的功能擴充套件或保護,而不需要修改原有的物件,符合開閉原則。
- 代理模式可以實現對真實物件的訪問控制或延遲載入,提高系統的效能和安全性。
- 代理模式可以實現對真實物件的透明訪問,客戶端只需要使用抽象主題的介面,而不需要關心真實物件和代理物件的細節。
缺點:
- 代理模式會增加系統的複雜度和開銷,因為需要建立和維護代理物件。
- 代理模式可能會降低系統的響應速度,因為每次呼叫真實物件的方法都需要經過代理物件。
動態代理模式
優點:
- 動態代理可以在執行時動態地建立代理物件,而不需要事先定義代理類,這樣可以減少程式碼量和提高開發效率。
- 動態代理可以根據不同的目標物件和需求,靈活地生成不同的代理物件,這樣可以增加系統的可擴充套件性和可維護性。
- 動態代理可以實現對目標物件的透明訪問,客戶端只需要使用抽象主題的介面,而不需要關心真實物件和代理物件的細節。
缺點:
- 動態代理需要使用反射和位元組碼技術來生成代理物件,這樣會增加系統的複雜度和開銷,也可能會影響系統的效能和穩定性。
- 動態代理需要遵循一些約束和限制,例如JDK動態代理只能代理實現了介面的類,CGLIB動態代理不能代理final類或方法等,這樣會降低系統的靈活性和通用性。
- 動態代理比靜態代理更難以理解和掌握,需要有一定的基礎知識和經驗才能使用好動態代理。
運用場景
- 當我們需要訪問一個遠端物件時,可以使用遠端代理,它可以隱藏遠端物件的位置和通訊細節,讓客戶端像訪問本地物件一樣訪問遠端物件。
- 當我們需要建立一個開銷很大的物件時,可以使用虛擬代理,它可以在真正需要的時候才建立真實物件,從而實現延遲載入和節省資源。
- 當我們需要給一個物件增加一些額外的功能時,可以使用裝飾代理,它可以在不修改原有物件的情況下,給物件新增一些新的行為或屬性。
- 當我們需要給一個物件增加一些訪問控制或安全保護時,可以使用保護代理,它可以根據不同的使用者或角色,對物件的訪問進行限制或檢查。
- 當我們需要給一個物件增加一些日誌記錄或效能監控時,可以使用日誌代理或效能代理,它可以在呼叫物件的方法之前或之後,記錄相關的資訊或資料。
總結
代理模式是一種常用的結構型設計模式,它可以讓我們透過一個代理物件來間接訪問一個真實物件,從而實現對目標物件的功能擴充套件或保護。代理模式有三個主要角色:抽象主題、真實主題和代理主題。代理模式有多種型別,例如靜態代理、動態代理、遠端代理、虛擬代理等。代理模式有自己的優缺點,使用時需要根據具體的場景和需求來選擇合適的型別和方式。