1. 模式定義
為其他對象提供一種代理以控制對這個對象的訪問,分為靜態(tài)代理和動態(tài)代理,代理模式也被稱為委托模式,它是結(jié)構(gòu)型設(shè)計模式的一種。在現(xiàn)實生活中我們用到類似代理模式的場景有很多,比如代理上網(wǎng)、打官司等。編程的思想其實有的時候和生活差不多。
2. 代理模式的差異
靜態(tài)代理,在代碼運行前就已經(jīng)存在了代理類的class編譯文件;而動態(tài)代理則是在代碼運行時通過反射來動態(tài)地生成代理類的對象,并確定到底來代理誰。也就是我們在編碼階段無須知道代理誰,代理誰將會在代碼運行時決定。
在我們?nèi)粘i_發(fā)中,有一些類似于功能入口的列表,每個按鈕跳轉(zhuǎn)到不同的頁面,下面我們以頁面跳轉(zhuǎn)為例,用代理設(shè)計模式進行講解。
3. 靜態(tài)代理
IItemClick:提供跳轉(zhuǎn)到各個頁面的方法 的接口
/**
* 提供跳轉(zhuǎn)到各個頁面的方法 的接口
*/
public interface IItemClick {
void goToMain();
void goToDetail();
}
ButtonList:需要被代理跳轉(zhuǎn)操作的類
/**
* 需要被代理跳轉(zhuǎn)操作的類
*/
public class ButtonList implements IItemClick {
private int clickPosition;
public void setClickPosition(int clickPosition) {
this.clickPosition = clickPosition;
}
public int getClickPosition() {
return clickPosition;
}
@Override
public void goToMain() {
// 跳轉(zhuǎn)到首頁
}
@Override
public void goToDetail() {
// 跳轉(zhuǎn)到詳情頁
}
}
JumpStaticProxy:跳轉(zhuǎn)事務(wù)靜態(tài)代理類
/**
* 跳轉(zhuǎn)事務(wù)代理類
*/
public class JumpStaticProxy implements IItemClick {
private IItemClick iItemClick;
public JumpStaticProxy(@NonNull IItemClick iItemClick) {
this.iItemClick = iItemClick;
}
@Override
public void goToMain() {
iItemClick.goToMain();
}
@Override
public void goToDetail() {
iItemClick.goToDetail();
}
}
StaticProxyTestClient:用法,測試類
/**
* 測試類
*/
public class StaticProxyTestClient {
public static void main(String[] args){
ButtonList buttonList = new ButtonList();
JumpStaticProxy jumpStaticProxy = new JumpStaticProxy(buttonList);
if (buttonList.getClickPosition() == 0) {
jumpStaticProxy.goToMain();
} else if (buttonList.getClickPosition() == 1) {
jumpStaticProxy.goToDetail();
}
}
}
4. 動態(tài)代理
假設(shè)這時候,我們在列表中增加了一個按鈕,是跳轉(zhuǎn)到設(shè)置頁,那么要是在靜態(tài)代理的寫法需要同時改動 IItemClick、ButtonList、JumpStaticProxy,這樣的拓展性不高,那用動態(tài)代理怎樣寫呢?Java 提供了 動態(tài)的代理接口 InvocationHandler,實現(xiàn)該接口需要重寫invoke() 方法。
IItemClick:提供跳轉(zhuǎn)到各個頁面的方法 的接口
/**
* 提供跳轉(zhuǎn)到各個頁面的方法 的接口
*/
public interface IItemClick {
void goToMain();
void goToDetail();
// 增加的方法
void goToSetting();
}
ButtonList:需要被代理跳轉(zhuǎn)操作的類
/**
* 需要被代理跳轉(zhuǎn)操作的類
*/
public class ButtonList implements IItemClick {
private int clickPosition;
public void setClickPosition(int clickPosition) {
this.clickPosition = clickPosition;
}
public int getClickPosition() {
return clickPosition;
}
@Override
public void goToMain() {
// 跳轉(zhuǎn)到首頁
}
@Override
public void goToDetail() {
// 跳轉(zhuǎn)到詳情頁
}
@Override
public void goToSetting() {
// 跳轉(zhuǎn)到設(shè)置頁
}
}
JumpDynamicProxy:核心類,跳轉(zhuǎn)事務(wù)動態(tài)代理類
/**
* 跳轉(zhuǎn)事務(wù)動態(tài)代理類
*/
public class JumpDynamicProxy implements InvocationHandler {
/**
* 被代理的對象
*/
private Object mObject;
public JumpDynamicProxy(Object object) {
this.mObject = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 執(zhí)行方法 ,目標接口調(diào)用的方法都會來到這里面
// 調(diào)用被代理對象的方法
Object object = method.invoke(mObject, args);
return object;
}
}
DynamicProxyTestClient:動態(tài)代理用法介紹
public class DynamicProxyTestClient {
public static void main(String[] args) {
ButtonList buttonList = new ButtonList();
IItemClick itemClick =
// 返回的是 IBank 的一個實例對象,這個對象是由 Java 給我們創(chuàng)建的 ,調(diào)用的是 jni
(IItemClick) Proxy.newProxyInstance(
IItemClick.class.getClassLoader(), // ClassLoader
new Class<?>[]{IItemClick.class}, // 目標接口
new JumpDynamicProxy(buttonList) // JumpDynamicProxy (這個類是關(guān)鍵)
);
if (buttonList.getClickPosition() == 0) {
itemClick.goToMain();
} else if (buttonList.getClickPosition() == 1) {
itemClick.goToDetail();
} else {
itemClick.goToSetting();
}
}
}
5. 總結(jié)
從上面可以看出,動態(tài)代理的拓展性優(yōu)于靜態(tài)代理,靜態(tài)代理效率要高于動態(tài)代理,設(shè)計模式只是方便我們寫好代碼,靜態(tài)和動態(tài),需要讀者自行斟酌使用。