Android常見設(shè)計(jì)模式七:代理模式

對(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ù)膀跌。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市固灵,隨后出現(xiàn)的幾起案子捅伤,更是在濱河造成了極大的恐慌,老刑警劉巖巫玻,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丛忆,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡仍秤,警方通過查閱死者的電腦和手機(jī)熄诡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诗力,“玉大人凰浮,你說我怎么就攤上這事∥荆” “怎么了袜茧?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)瓣窄。 經(jīng)常有香客問我笛厦,道長(zhǎng),這世上最難降的妖魔是什么俺夕? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任裳凸,我火速辦了婚禮,結(jié)果婚禮上啥么,老公的妹妹穿的比我還像新娘登舞。我一直安慰自己贰逾,他們只是感情好悬荣,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疙剑,像睡著了一般氯迂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上言缤,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天嚼蚀,我揣著相機(jī)與錄音,去河邊找鬼管挟。 笑死轿曙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播导帝,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼守谓,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了您单?” 一聲冷哼從身側(cè)響起斋荞,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎虐秦,沒想到半個(gè)月后平酿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡悦陋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年蜈彼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叨恨。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡柳刮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出痒钝,到底是詐尸還是另有隱情秉颗,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布送矩,位于F島的核電站蚕甥,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏栋荸。R本人自食惡果不足惜菇怀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望晌块。 院中可真熱鬧爱沟,春花似錦、人聲如沸匆背。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)钝尸。三九已至括享,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間珍促,已是汗流浹背铃辖。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猪叙,地道東北人娇斩。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓仁卷,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親犬第。 傳聞我的和親對(duì)象是個(gè)殘疾皇子五督,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容