設計模式--代理模式

代理模式:給某一對象提供代理對象,並由代理對象控制具體對象的引用.

代理,指的就是一個角色代表另一個角色採取行動,就象生活中,一個紅酒廠商,是不會直接把紅酒零售客戶的,都是通過代理來完成他的銷售業務的.而客戶,也不用爲了喝紅酒而到處找工廠,他只要找到廠商在當地的代理就行了,具體紅酒工廠在那裏,客戶不用關心,代理會幫他處理.

[size=large][color=red]代理模式涉及的角色:[/color][/size]
[color=red]1:抽象主題角色[/color].聲明瞭代理主題和真實主題的公共接口,使任何需要真實主題的地方都能用代理主題代替.

[color=red]2:代理主題角色[/color].含有真實主題的引用,從而可以在任何時候操作真實主題,代理主題功過提供和真實主題相同的接口,使它可以隨時代替真實主題.代理主題通過持有真實主題的引用,不但可以控制真實主題的創建或刪除,可以在真實主題被調用前進行攔截,或在調用後進行某些操作.

[color=red]3:真實代理對象[/color].定義了代理角色所代表的具體對象.

下面是代理模式的實現類圖:
[img]http://dl.iteye.com/upload/attachment/168091/34067706-fd9f-31c3-9ecd-3d6cab203286.jpg[/img]

根據上圖的關係,我們可以用客戶買紅酒來模擬代理模式的實現,

紅酒代理商和紅酒廠商都有銷售紅酒的只能,我們可以爲他們定義一個共同的抽象主題角色,

/**
*抽象主題角色,定義了真實角色和代理角色的公共接口
*/
public interface SellInterface{
public Object sell();
}


接着,我們定義真實主題角色(這裏就是紅酒工廠),它必須實現了SellInterface接口的.

/**
*真實主題角色,這裏指紅酒工廠角色,它實現了SellInterface接口
*/
public class RedWineFactory implements SellInterface{
public Object sell(){
System.out.println("真實主題角色RedWineFactory 被調用了");
return new Object();
}
}


下面是代理主題角色(這裏指紅酒代理商),同樣,代理主題也必須實現SellInterface接口.

/**
*代理主題角色,這裏指紅酒代理商.它除了也要實現了sellInterface接口外,還持有紅酒
*廠商RedWineFactory 對象的引用,從而使它能在調用真實主題前後做一些必要處理.
*/
public class RedWineProxy implements SellInterface{
//持有一個RedWineFactory對象的引用
private RedWineFactory redWineFactory;

//銷售總量
private static int sell_count = 0;

public Object sell(){
if(checkUser()){//在通過代理主題角色,我們可以在真實主題角色被調用前做一些諸如權限判斷的事情
Object obj = redWineFactory.sell();
count ++;//同樣,在調用後我們也可以執行一些額外的動作.
return obj ;
}else{
throw new RuntimeException();
}
}

protected boolean checkUser(){
//do something
return true;
}
}


接下來看看調用代理對象的代碼

public static void main(String agr[])
{
SellInterface sell = new RedWineProxy();
sell.sell();
}


從上面的例子可以看出代理模式的工作方式,首先,因爲代理主題和真實主題都實現了共同的接口,這使我們可以在不改變原來接口的情況下,只要用真實主題對象的地方,都可以用代理主題來代替.其次,代理主題在客戶和真實主題之間起了一箇中介作用,利用這個中介平臺,我們可以在把客戶請求傳遞給真實主題之前,做一些必要的預處理.

[size=large][color=red]java對代理模式的支持 ---動態代理[/color][/size]
上面的代理,我們強迫代理類RedWineProxy實現了抽象接口SellInterface.這導致我們的代理類無法通用於其他接口,所以不得不爲每一個接口實現一個代理類.幸好,java爲代理模式提供了支持.
java主要是通過Proxy類和InvocationHandler接口來給實現對代理模式的支持的.
下面用java的代理機制來實現上面的例子

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
*代理類一定要實現了InvocationHandler接口
*/
public class ProxyObject implements InvocationHandler{
private Object proxy_obj;

ProxyObject(Object obj){
this.proxy_obj = obj;
}

public static Object factory(Object obj){
Class cls = obj.getClass();
//通過Proxy類的newProxyInstance方法來返回代理對象
return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),new ProxyObject(obj));
}

/**
*實現InvocationHandler接口的invoke
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("函數調用前被攔截了: " + method);
if(args != null){
//打印參數列表
System.out.println("方法有 " + args.length + " 個參數");
for(int i = 0; i < args.length; i ++){
System.out.println(args[i]);
}
}
//利用反射機制動態調用原對象的方法
Object mo = method.invoke(proxy_obj, args);
System.out.println("函數調用後進行處理 : " + method);
return mo;
}

//測試代碼
public static void main(String agr[]){
SellInterface si = (SellInterface)factory(new RedWineFactory());
si.sell();
}
}

通過上面的代碼可以看出,代理主題ProxyObject類並沒有實現我們定義的SellInterface藉口,
而是實現了java的InvocationHandler接口,這樣就把代理主題角色和我們的業務代碼分離開來,使代理對象能通用於其他接口.
其實InvocationHandler接口就是一種攔截機制,當系統中有了代理對象以後,對原對象(真實主題)方法的調用,都會轉由InvocationHandler接口來處理,並把方法信息以參數的形式傳遞給invoke方法,這樣,我們就可以在invoke方法中攔截原對象的調用,並通過反射機制來動態調用原對象的方法.這好象也是spring aop編程的基礎吧

[size=large][color=red]接着,用代理模式實現一個超級簡單的aop攔截機制[/color][/size]
這個例子可以攔截我們指定的函數,並在攔截前後根據需要進行處理

/**
*切面接口,通過實現這個接口,我們可以對指定函數在調用前後進行處理
*/
public interface AopInterface {
public void before(Object obj);//調用的處理
public void end(Object obj);//調用後的處理
}


這個是實現了AopInterface 接口,在這裏我們實現了我們的處理邏輯

public class AopInterfaceImp implements AopInterface{

public void before(Object obj) {
System.out.println("調用前攔截");
}

public void end(Object obj) {
System.out.println("調用調用後處理");
}

}


接着是代理類

public class PeoxyObject implements InvocationHandler {
private AopInterface aop;//定義了切入時調用的方法
private Object proxy_obj;
private String methodName;//指定要切入的方法名

PeoxyObject(){}

public Object factory(Object obj){
proxy_obj = obj;
Class cls = obj.getClass();
return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),this);
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(this.aop == null)throw new NullPointerException("aop is null");
if(method == null)throw new NullPointerException("method is null");

Object o;
//如果指定了要攔截方法名,並且調用的方法和指定的方法名相同,則進行攔截處理
//否則當正常方法處理
if(methodName != null && method.toString().indexOf(methodName) != -1){
aop.before(proxy_obj);//指定方法調用前的處理
o = method.invoke(proxy_obj, args);
aop.end(proxy_obj);//指定方法調用後的處理
}else{
//沒有指定的方法,以正常方法調用
o = method.invoke(proxy_obj, args);
}
return o;
}

public AopInterface getAop() {
return aop;
}

public void setAop(AopInterface aop) {
this.aop = aop;
}

public String getMethodName() {
return methodName;
}

public void setMethodName(String methodName) {
this.methodName = methodName;
}
}


這裏定義一個用來測試用的類

public interface SubInterface {
public void add(String value1,String value2);
public void acc(String value1);
}

public class ImpObject implements SubInterface{

public void add(String value1,String value2) {
System.out.println("ImpObject add(String value1,String value2)");
}

public void acc(String value1){
System.out.println("ImpObject acc(String value1)");
}

}


這裏是測試代碼

public static void main(String agr[]){
PeoxyObject po = new PeoxyObject();

po.setAop(new AopInterfaceImp());//我們實現的攔截處理對象
po.setMethodName("acc");//指定要攔截的函數

SubInterface si = (SubInterface)po.factory(new ImpObject());
//因爲add方法不是我們指定的攔截函數,AopInterfaceImp是不會被執行
si.add("tt","dd");

//acc是我們指定的攔截方法,所以調用acc的前後會先執行AopInterfaceImp
//對象的兩個方法
si.acc("tt");
}


通過上面可以看出,攔截機制是代理模式的重要使用方式之一,
除了攔截,代理模式還常用於資源加載,當我們要加載的資源很大時,我們可以讓真實主題角色在後臺加載資源,讓代理主題角色負責處理前臺的等待提示信息.
還有就是授權機制,通過代理能攔截真實主題的能力,來控制真實主題的訪問權限.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章