"Java代理設計模式 靜態代理" "Java中的動態代理 調用處理器" 代理設計模式的UML圖: 我將首先介紹Java中的各種代理實現方法 Java代理設計模式 靜態代理 這個例子非常簡單,只有一個方法 的介面 : 測試代碼: 測試輸出: 現在麻煩的是,Jerry的領導因為團隊中的開發者像Jerr ...
代理設計模式的UML圖:
我將首先介紹Java中的各種代理實現方法
Java代理設計模式 - 靜態代理
這個例子非常簡單,只有一個方法wirteCode
的介面IDeveloper
:
public interface IDeveloper {
public void writeCode();
}
// 實現這個介面的類:
public class Developer implements IDeveloper{
private String name;
public Developer(String name){
this.name = name;
}
@Override
public void writeCode() {
System.out.println("Developer " + name + " writes code");
}
}
測試代碼:
public class DeveloperTest {
public static void main(String[] args) {
IDeveloper jerry = new Developer("Jerry");
jerry.writeCode();
}
}
測試輸出:
Developer Jerry writes code
現在麻煩的是,Jerry的領導因為團隊中的開發者像Jerry一樣沒有編寫技術文檔,所以並不滿意。經過討論後,整個團隊達成協議,相關文檔必須與代碼一起提供。
為了迫使開發人員編寫文檔而不直接對現有的實現類Developer進行修改,現在就可以使用靜態代理來實現:
// 創建一個和Developer類實現同樣介面的類
public class DeveloperProxy implements IDeveloper{
private IDeveloper developer;
// 引用Developer類對象
public DeveloperProxy(IDeveloper developer){
this.developer = developer;
}
@Override
public void writeCode() {
System.out.println("Write documentation...");
this.developer.writeCode();
}
}
測試代碼:
public class DeveloperTest {
public static void main(String[] args) {
Developer jerry = new Developer("jerry");
DeveloperProxy jerryproxy = new DeveloperProxy(jerry);
jerryproxy.writeCode();
}
}
測試輸出:
Write documentation...
Developer jerry writes code
靜態代理的優點
假設你希望在不修改原始類代碼的情況下增強現有的穩定實現,你可以創建一個代理類,並將原始實現封裝為代理中的私有屬性。增強的功能是在代理類中完成的,對現有的代碼是完全透明的。回到上面的示例,客戶端代碼並不關心它用來調用writeCode()
方法的變數是否指向真正的開發人員或開發人員代碼。
優點:
- 易於實施和理解
- 原始實現與其代理之間的關係在編譯時已經確定,運行時沒有額外的開銷。
靜態代理的缺點
我們仍然使用這個例子來說明。
假設現在缺失文檔的問題在QA同事中仍然存在。如果我們想通過靜態代理來解決這個問題,那麼必須引入另一個代理類。
這是測試人員的介面:
public interface ITester {
public void doTesting();
}
// ITester 介面的實現類:
public class Tester implements ITester {
private String name;
public Tester(String name){
this.name = name;
}
@Override
public void doTesting() {
System.out.println("Tester " + name + " is testing code");
}
}
測試人員代理:
public class TesterProxy implements ITester{
private ITester tester;
public TesterProxy(ITester tester){
this.tester = tester;
}
@Override
public void doTesting() {
System.out.println("Tester is preparing test documentation...");
tester.doTesting();
}
}
測試代碼和輸出:
從Tester代理的源代碼中我們可以很容易的觀察到它與開發人員具有完全相同的邏輯。如果又過了一段時間,我們必須為軟體交付過程中的其他同事建立文檔,我們必須一次又一次的引入新的靜態代理類,這會導致靜態代理類變得十分龐大。
Java中的動態代理 - 調用處理器
現在我通過代理類EnginnerProxy
來為所有的具體角色提供代理服務,而不是單獨為每個原始實現類設置專用的靜態代理類。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class EnginnerProxy implements InvocationHandler
{
Object obj;
public Object bind(Object obj)
{
this.obj = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
System.out.println("Enginner writes document");
Object res = method.invoke(obj, args);
return res;
}
}
主要筆記:
- 不是從具有業務介面(IDeveloper或ITester)的專用介面繼承,而是在此變體中,通過代理繼承自JDK提供的技術介面
InvocationHandler
。 - 為了確保通用代理可以適用於所有可能的具體實現類,在代理中定義了具有泛型類型的
Object
變數。 - 調用代理實例的介面方法時,它將被
InvocationHandler
中定義的invoke
方法攔截,其中由應用程式開發人員聲明的增強邏輯與由Java Reflection
調用的原始邏輯一起調用。
下麵是如何使用InvocationHandler設計的動態代理和測試輸出:
動態代理類的限制
雖然這個變體成功的避免了靜態代理中的重覆缺陷,但是它仍然有一個局限性,它無法使用不是從介面繼承的實現類,就是說,使用動態代理類,原始類必須先要實現一個或多個介面,這個介面也就是代理介面。
考慮下麵的例子,產品所有者沒有實現任何介面:
public class ProductOwner {
private String name;
public ProductOwner(String name){
this.name = name;
}
public void defineBackLog(){
System.out.println("PO: " + name + " defines Backlog.");
}
}
以下代碼在IDE中沒有任何語法錯誤:
ProductOwner po = new ProductOwner("Ross");
ProductOwner poProxy = (ProductOwner) new EnginnerProxy().bind(po);
poProxy.defineBackLog();
不幸的是編譯時報出了以下錯誤: