代理模式(Proxy Pattern)
是程式設計中的一種設計模式,其主要作用是為其他物件提供一種代理,以控制對這個物件的訪問。以下是關於代理模式的詳細解釋:
代理模式定義:為其他物件提供一種代理以控制對這個物件的訪問。在某些情況下,一個物件不適合或者不能直接引用另一個物件,而代理物件可以在客戶端和目標物件之間起到中介的作用。
一、組成
- 抽象角色:透過介面或抽象類宣告真實角色實現的業務方法。
- 代理角色:實現抽象角色,是真實角色的代理,透過真實角色的業務邏輯方法來實現抽象方法,並可以附加自己的操作。
- 真實角色:實現抽象角色,定義真實角色所要實現的業務邏輯,供代理角色呼叫。
二、優點
- 職責清晰:真實的角色就是實現實際的業務邏輯,不用關心其他非本職責的事務,透過後期的代理完成一件完整事務,附帶的結果就是程式設計簡潔清晰。
- 中介和保護作用:代理物件可以在客戶端和目標物件之間起到中介的作用,起到了中介的作用和保護了目標物件的作用。
- 高擴充套件性:模式結構包括真正的你要訪問的物件(目標類)和代理物件,它們實現同一個介面,先訪問代理類再訪問真正要訪問的物件。
三、缺點
- 請求處理速度可能變慢:由於在客戶端和真實主題之間增加了代理物件,因此有些型別的代理模式可能會造成請求的處理速度變慢。
- 實現複雜度:實現代理模式需要額外的工作,有些代理模式的實現可能非常複雜。
四、實現方式
代理模式通常有兩種實現方式:靜態代理和動態代理。
- 靜態代理:在編譯時就已經確定代理類,代理類和目標類實現了相同的介面,客戶端透過代理類來呼叫目標類的方法。靜態代理需要手動編寫代理類,並且對於每個目標類都需要編寫一個對應的代理類,因此當目標類較多時,工作量會很大。
- 動態代理:動態代理是在執行時動態生成代理類,客戶端透過動態代理類來呼叫目標類的方法。Java 中提供了兩種動態代理的實現方式:基於介面的 JDK 動態代理和基於類的 CGLIB 動態代理。JDK 動態代理要求目標類必須實現一個或多個介面,而 CGLIB 動態代理則要求目標類不能被 final 修飾。
五、示例(以 JDK 動態代理為例)
以下是一個使用 JDK 動態代理實現代理模式的示例:
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 抽象主題角色
interface Subject {
void request();
}
// 真實主題角色
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
// 代理主題角色
class ProxyHandler implements InvocationHandler {
private Object target;
public ProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(target, args);
after();
return result;
}
private void before() {
System.out.println("ProxyHandler: Before method call.");
}
private void after() {
System.out.println("ProxyHandler: After method call.");
}
}
// 客戶端程式碼
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
InvocationHandler handler = new ProxyHandler(realSubject);
// 建立代理物件
Subject proxySubject = (Subject) Proxy.newProxyInstance(
RealSubject.class.getClassLoader(),
RealSubject.class.getInterfaces(),
handler
);
// 呼叫代理物件的方法
proxySubject.request();
}
}