代理模式也叫委托模式调缨,是結(jié)構(gòu)型設(shè)計模式疮鲫。代理就是讓別人幫你做事,比如幫你帶飯弦叶,請律師打官司什么的俊犯。
定義
為其他對象提供一種代理以控制對這個對象的訪問。
使用場景
- 當(dāng)一個對象不能或者不想直接訪問另一個對象時湾蔓,可以通過一個代理對象來間接訪問瘫析。為保證客戶端使用的透明性,委托對象和代理對象要實現(xiàn)同樣的接口默责。
- 被訪問的對象不想暴露全部內(nèi)容時贬循,可以通過代理去掉不想被訪問的內(nèi)容。
UML
- Subject: 抽象主題類桃序,聲明真是主體與代理主題的共同接口方法杖虾。
- RealSubject: 真實主題類,定義了代理所表示的真是對象媒熊,執(zhí)行具體的業(yè)務(wù)方法奇适。客戶端通過代理類來間接的調(diào)動這個真實主題中的方法芦鳍。
- ProxySubject: 代理類嚷往,持有一個真實類的引用,在接口方法中調(diào)用真實主題相應(yīng)的方法柠衅,達(dá)到代理的作用皮仁。
簡單實現(xiàn)
就以打官司為例。我們一般人要打官司都要找個律師來代理菲宴。
靜態(tài)代理
先建立一個起訴類的接口:
public interface ILawsuit {
void submit();//提交申請
void burden();//進(jìn)行舉證
void defend();//開始辯護(hù)
void finish();//訴訟完成
}
真正的起訴者:
public class Civilian implements ILawsuit {
@Override
public void submit() {
System.out.println("起訴");
}
@Override
public void burden() {
System.out.println("舉證");
}
@Override
public void defend() {
System.out.println("辯護(hù)");
}
@Override
public void finish() {
System.out.println("勝訴");
}
}
找的律師:
public class Lawyer implements ILawsuit {
private ILawsuit civilian;
public Lawyer(ILawsuit civilian) {
this.civilian = civilian;
}
@Override
public void submit() {
civilian.submit();
}
@Override
public void burden() {
civilian.burden();
}
@Override
public void defend() {
civilian.defend();
}
@Override
public void finish() {
civilian.finish();
}
}
客戶端調(diào)用贷祈,調(diào)用律師的方法,通過律師調(diào)用真正的su起訴者的方法喝峦。
public class Client {
public static void main(String[] args) {
ILawsuit civilian = new Civilian();
ILawsuit lawyer = new Lawyer(civilian);
lawyer.submit();
lawyer.burden();
lawyer.defend();
lawyer.finish();
}
}
輸出:
一個代理可以代理多個類势誊,就像這個律師可以給很多人打官司,只需要在實現(xiàn)一個具體的ILawsuit就行了谣蠢。代理會根據(jù)傳進(jìn)來的被代理者調(diào)用傳進(jìn)來的被代理者的方法粟耻。
動態(tài)代理
代理模式大致分為兩大部分:靜態(tài)代理和動態(tài)代理。
上面是是一種靜態(tài)代理眉踱,代理者的代碼時先生成寫好勋颖,然后再對其進(jìn)行編譯,在代碼運行前勋锤,代理類的class編譯文件就已經(jīng)存在了饭玲。
動態(tài)代理是相反的,通過反射動態(tài)的生成代理者對象叁执,也就是說在寫代碼的時候根本不知道要代理誰茄厘,具體代理誰會在執(zhí)行階段決定矮冬。
Java提供了一個便捷的動態(tài)代理接口InvocationHandler,動態(tài)代理類只要實現(xiàn)這個接口就行:
public class DynamicProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}
看一下動態(tài)代理的用法:
public class DynamicProxy implements InvocationHandler {
private Object object;
public DynamicProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//當(dāng)然這里可以對方法名進(jìn)行判斷過濾 if(method.getName().equals("***"))
Object result = method.invoke(object,args);
return result;
}
}
客戶端調(diào)用:
public class Main {
public static void main(String[] args) {
ILawsuit lawsuit = new Civilian();
DynamicProxy proxy = new DynamicProxy(lawsuit);
ClassLoader loader = lawsuit.getClass().getClassLoader();
//動態(tài)創(chuàng)建代理類次哈,需要傳入一個類加載器ClassLoader胎署;一個你希望這個代理實現(xiàn)的接口列表,這里要代理ILawsuit接口窑滞;
//和一個InvocationHandler的實現(xiàn)琼牧,也就是前面創(chuàng)建的proxy。
ILawsuit lawyer = (ILawsuit) Proxy.newProxyInstance(loader,new Class[]{ILawsuit.class},proxy);
lawyer.submit();
lawyer.burden();
lawyer.defend();
lawyer.finish();
}
}
輸出和上面一毛一樣:
動態(tài)代理并不局限與代理一個接口的實現(xiàn)哀卫,可以根據(jù)運行時傳入的接口巨坊,動態(tài)的生成代理類,然后通過Method的invoke方法來執(zhí)行被代理類的真實方法此改。非常靈活趾撵。
其他分類
靜態(tài)代理和動態(tài)代理是從code方便進(jìn)行分類的。這兩個分類根據(jù)適用范圍來分都可以分為下面幾種:
- 遠(yuǎn)程代理:為摸個對象在不同的內(nèi)存地址空間提供局部代理共啃,是系統(tǒng)Server部分隱藏占调,以便Client不用考慮Server的存在。
- 虛擬代理:如果要創(chuàng)建一個資源消耗較大的對象移剪,可以先用一個代理對象表示究珊,在真正需要的時候才真正創(chuàng)建。
- 保護(hù)代理:用代理對象控制對一個對象的訪問纵苛,給不同的用戶提供不同的訪問權(quán)限剿涮。
- 智能引用:在引用原始對象的時候附加額外操作,并對指向原始對象的引用增加引用計數(shù)赶站。
總結(jié)
代理模式使用非常廣泛,從分類就能感覺出來纺念,而且其他的設(shè)計模式中也會有代理模式的影子贝椿。
優(yōu)點
優(yōu)點可以從他的適用范圍看出來
- 協(xié)調(diào)調(diào)用者和被調(diào)用者,降低系統(tǒng)耦合度陷谱。
- 用小對象代表大對象烙博,減少系統(tǒng)資源消耗,提高系統(tǒng)運行速度烟逊,如虛擬代理渣窜。
- 控制用戶對唄調(diào)用者的使用權(quán)限,如保護(hù)代理宪躯。
缺點
- 首先當(dāng)然是比直接調(diào)用原始對象多了一個中間者乔宿,會讓結(jié)構(gòu)有點復(fù)雜。
- 調(diào)用原始對象的方法要通過代理來調(diào)用访雪,可能會造成請求處理速度變慢详瑞。