對(duì)于開發(fā)人員來說朽肥,設(shè)計(jì)模式有時(shí)候就是一道坎菇民,但是設(shè)計(jì)模式又非常有用尽楔,過了這道坎,它可以讓你水平提高一個(gè)檔次第练。而在android開發(fā)中阔馋,必要的了解一些設(shè)計(jì)模式又是必須的,因?yàn)樵O(shè)計(jì)模式在Android源碼中娇掏,可以說是無處不在呕寝。對(duì)于想系統(tǒng)的學(xué)習(xí)設(shè)計(jì)模式的同學(xué),這里推薦一本書婴梧,《大話設(shè)計(jì)模式》下梢。
Android常用設(shè)計(jì)模式系列:
面向?qū)ο蟮幕A(chǔ)特征
面向?qū)ο蟮脑O(shè)計(jì)原則
單例模式
模板模式
適配器模式
工廠模式
代理模式
原型模式
策略模式
Build模式
觀察者模式
裝飾者模式
中介模式
門面模式
代理模式
代理模式是非常常見的設(shè)計(jì)模式之一客蹋,寫個(gè)筆記,記錄一下我的學(xué)習(xí)過程和心得孽江。
首先了解一些代理模式的定義讶坯。
為其他對(duì)象提供一種代理以控制這個(gè)對(duì)象的訪問。
涉及角色及說明:
Subject(抽象主題類):接口或者抽象類岗屏,聲明真實(shí)主題與代理的共同接口方法辆琅。
RealSubject(真實(shí)主題類):也叫做被代理類或被委托類,定義了代理所表示的真實(shí)對(duì)象这刷,負(fù)責(zé)具體業(yè)務(wù)邏輯的執(zhí)行婉烟,客戶端可以通過代理類間接的調(diào)用真實(shí)主題類的方法。
Proxy(代理類):也叫委托類暇屋,持有對(duì)真實(shí)主題類的引用似袁,在其所實(shí)現(xiàn)的接口方法中調(diào)用真實(shí)主題類中相應(yīng)的接口方法執(zhí)行。
Client(客戶端類):使用代理模式的地方咐刨。
理解:
- 代理模式屬于結(jié)構(gòu)型模式昙衅。
- 代理模式也叫委托模式。
- 生活中所宰,比如代購(gòu)、打官司等等畜挥,實(shí)際上都是一種代理模式仔粥。
以海外代購(gòu)為例,在國(guó)內(nèi)的人想買國(guó)外的東西只能去找國(guó)外的人去進(jìn)行代購(gòu)蟹但。
1 創(chuàng)建抽象主題類
人都是有購(gòu)買這個(gè)方法的:
public interface People {
void buy();//購(gòu)買
}
2 創(chuàng)建真實(shí)主題類
國(guó)內(nèi)的人想購(gòu)買某些產(chǎn)品躯泰,定義具體的購(gòu)買過程:
public class Domestic implements People {
@Override
public void buy() {//具體實(shí)現(xiàn)
System.out.println("國(guó)內(nèi)要買一個(gè)包");
}
}
3 創(chuàng)建代理類
海外的代購(gòu)黨需要知道是誰(shuí)(持有真實(shí)主題類的引用)想購(gòu)買啥產(chǎn)品:
public class Oversea implements People {
People mPeople;//持有People類的引用
public Oversea(People people) {
mPeople = people;
}
@Override
public void buy() {
System.out.println("我是海外代購(gòu):");
mPeople.buy();//調(diào)用了被代理者的buy()方法,
}
}
5 客戶端測(cè)試:
public void test() {
People domestic = new Domestic(); //創(chuàng)建國(guó)內(nèi)購(gòu)買人
People oversea = new Oversea(domestic); //創(chuàng)建海外代購(gòu)類并將domestic作為構(gòu)造函數(shù)傳遞
oversea.buy(); //調(diào)用海外代購(gòu)的buy()
}
輸出結(jié)果:
我是海外代購(gòu):
國(guó)內(nèi)要買一個(gè)包
靜態(tài)代理與動(dòng)態(tài)代理
從代碼的角度來分,代理可以分為兩種:一種是靜態(tài)代理华糖,另一種是動(dòng)態(tài)代理麦向。
- 靜態(tài)代理就是在程序運(yùn)行前就已經(jīng)存在代理類的字節(jié)碼文件,代理類和委托類的關(guān)系在運(yùn)行前就確定了客叉。上面的例子實(shí)現(xiàn)就是靜態(tài)代理诵竭。
- 動(dòng)態(tài)代理類的源碼是在程序運(yùn)行期間根據(jù)反射等機(jī)制動(dòng)態(tài)的生成,所以不存在代理類的字節(jié)碼文件兼搏。代理類和委托類的關(guān)系是在程序運(yùn)行時(shí)確定卵慰。
??下面我們實(shí)現(xiàn)動(dòng)態(tài)代理,Java提供了動(dòng)態(tài)的代理接口InvocationHandler佛呻,實(shí)現(xiàn)該接口需要重寫invoke()方法:
1 創(chuàng)建動(dòng)態(tài)代理類
public class DynamicProxy implements InvocationHandler {//實(shí)現(xiàn)InvocationHandler接口
private Object obj;//被代理的對(duì)象
public DynamicProxy(Object obj) {
this.obj = obj;
}
//重寫invoke()方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("海外動(dòng)態(tài)代理調(diào)用方法: "+method.getName());
Object result = method.invoke(obj, args);//調(diào)用被代理的對(duì)象的方法
return result;
}
}
2 修改客戶端的測(cè)試方法:
public void test() {
People domestic = new Domestic(); //創(chuàng)建國(guó)內(nèi)購(gòu)買人
DynamicProxy proxy = new DynamicProxy(domestic); //創(chuàng)建動(dòng)態(tài)代理
ClassLoader classLoader = domestic.getClass().getClassLoader(); //獲取ClassLoader
People oversea = (People) Proxy.newProxyInstance(classLoader, new Class[]{People.class}, proxy); //通過 Proxy 創(chuàng)建海外代購(gòu)實(shí)例 裳朋,實(shí)際上通過反射來實(shí)現(xiàn)的。
oversea.buy();//調(diào)用海外代購(gòu)的buy()
}
輸出結(jié)果:
海外動(dòng)態(tài)代理調(diào)用方法: buy
國(guó)內(nèi)要買一個(gè)包
靜態(tài)代理與動(dòng)態(tài)代理比較
靜態(tài)代理的缺點(diǎn):
靜態(tài)代理如果接口新增一個(gè)方法吓著,除了所有實(shí)現(xiàn)類(真實(shí)主題類)需要實(shí)現(xiàn)這個(gè)方法外鲤嫡,所有代理類也需要實(shí)現(xiàn)此方法送挑。增加了代碼維護(hù)的復(fù)雜度。
代理對(duì)象只服務(wù)于一種類型的對(duì)象暖眼,如果要服務(wù)多類型的對(duì)象惕耕。必須要為每一種對(duì)象都進(jìn)行代理,靜態(tài)代理在程序規(guī)模稍大時(shí)就無法勝任了罢荡。
動(dòng)態(tài)代理的優(yōu)點(diǎn):
可以通過一個(gè)代理類完成全部的代理功能赡突,接口中聲明的所有方法都被轉(zhuǎn)移到調(diào)用處理器一個(gè)集中的方法中處理(InvocationHandler.invoke)。當(dāng)接口方法數(shù)量較多時(shí)区赵,我們可以進(jìn)行靈活處理惭缰,而不需要像靜態(tài)代理那樣每一個(gè)方法進(jìn)行中轉(zhuǎn)。
動(dòng)態(tài)代理的應(yīng)用使我們的類職責(zé)更加單一笼才,復(fù)用性更強(qiáng)漱受。
動(dòng)態(tài)代理的缺點(diǎn):
不能對(duì)類進(jìn)行代理,只能對(duì)接口進(jìn)行代理骡送,如果我們的類沒有實(shí)現(xiàn)任何接口昂羡,那么就不能使用這種方式進(jìn)行動(dòng)態(tài)代理(因?yàn)?Proxy()這個(gè)類集成了Proxy,Java的集成不允許出現(xiàn)多個(gè)父類)。
廣泛應(yīng)用
總結(jié)
總結(jié)一下代理模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
代理作為調(diào)用者和真實(shí)主題的中間層,降低了模塊間和系統(tǒng)的耦合性摔踱。
可以以一個(gè)小對(duì)象代理一個(gè)大對(duì)象,達(dá)到優(yōu)化系統(tǒng)提高運(yùn)行速度的目的虐先。
代理對(duì)象能夠控制調(diào)用者的訪問權(quán)限,起到了保護(hù)真實(shí)主題的作用派敷。
缺點(diǎn)
由于在調(diào)用者和真實(shí)主題之間增加了代理對(duì)象蛹批,因此可能會(huì)造成請(qǐng)求的處理速度變慢。
實(shí)現(xiàn)代理模式需要額外的工作(有些代理模式的實(shí)現(xiàn)非常復(fù)雜)篮愉,從而增加了系統(tǒng)實(shí)現(xiàn)的復(fù)雜度腐芍。
適用場(chǎng)景
當(dāng)一個(gè)對(duì)象不能或者不想直接訪問另一個(gè)對(duì)象時(shí),可以通過一個(gè)代理對(duì)象來間接訪問试躏。為保證客戶端使用的透明性猪勇,委托對(duì)象和代理對(duì)象要實(shí)現(xiàn)同樣的接口。
被訪問的對(duì)象不想暴露全部?jī)?nèi)容時(shí)颠蕴,可以通過代理去掉不想被訪問的內(nèi)容泣刹。
根據(jù)適用范圍,代理模式可以分為以下幾種:
- 遠(yuǎn)程代理:為一個(gè)對(duì)象在不同的地址空間提供局部代表犀被,這樣系統(tǒng)可以將Server部分的事項(xiàng)隱藏项玛。
- 虛擬代理:如果要?jiǎng)?chuàng)建一個(gè)資源消耗較大的對(duì)象,可以先用一個(gè)代理對(duì)象表示弱判,在真正需要的時(shí)候才真正創(chuàng)建襟沮。
- 保護(hù)代理:用代理對(duì)象控制對(duì)一個(gè)對(duì)象的訪問,給不同的用戶提供不同的訪問權(quán)限。
- 智能引用:在引用原始對(duì)象的時(shí)候附加額外操作开伏,并對(duì)指向原始對(duì)象的引用增加引用計(jì)數(shù)膀跌。