android設(shè)計(jì)模式之代理模式

靜態(tài)代理

基本概念

目標(biāo)對(duì)象為其對(duì)象提供一種代理對(duì)象,其他對(duì)象通過代理對(duì)象來控制對(duì)目標(biāo)對(duì)象的訪問完残。

角色劃分

Proxy(美['prɑksi]):代理對(duì)象,其他對(duì)象直接控制的對(duì)象侮邀。
Subject([?s?bd??kt]):目標(biāo)接口绊茧,目標(biāo)對(duì)象的抽象。
RealSubject:具體目標(biāo)對(duì)象毅哗,目標(biāo)接口的實(shí)現(xiàn),即真正控制的對(duì)象仑乌。

簡(jiǎn)單理解:
當(dāng)其他對(duì)象對(duì)某個(gè)目標(biāo)對(duì)象進(jìn)行操作且需要對(duì)目標(biāo)對(duì)象進(jìn)行更改時(shí)晰甚,若目標(biāo)對(duì)象被多個(gè)對(duì)象引用厕九,或目標(biāo)對(duì)象不可以更改扁远,此時(shí)使用代理模式,通過對(duì)代理對(duì)象的修改并闲,而完成最終的業(yè)務(wù)需求焙蚓。

來源于生活的案例

  • 案例描述: 我通過代購(gòu)洒宝,買了一臺(tái)iphone X雁歌。

我想要買一臺(tái)港版的iphone X靠瞎,但是我在大陸,不能直接購(gòu)買港版的iphone X佳窑。于是神凑,我找到了代購(gòu)公司何吝,代購(gòu)公司幫我買一個(gè)iphoneX爱榕。

目標(biāo)接口:買手機(jī)動(dòng)作,行為抽象
目標(biāo)對(duì)象:我->買手機(jī)的人->買手機(jī)的行為
代理對(duì)象:代購(gòu)的人->同學(xué)或者朋友->代理買手機(jī)的行為
其他對(duì)象:調(diào)用購(gòu)買手機(jī)的對(duì)象
  • 案例理解: 我為什么是目標(biāo)對(duì)象藻三?

    在考慮整個(gè)案例之后棵帽,大多數(shù)人一定會(huì)主觀認(rèn)為目標(biāo)對(duì)象應(yīng)該是手機(jī)岖寞,但是將每個(gè)對(duì)象進(jìn)行行為劃分仗谆,即可理解為目標(biāo)對(duì)象是我,即我買手機(jī)的這個(gè)行為隶垮。
    整個(gè)案例的核心勉耀,是我想要買手機(jī)便斥,代購(gòu)的核心是幫我買手機(jī)威始,而手機(jī)卻是具有其他行為的黎棠,與買手機(jī)行為無關(guān)的對(duì)象脓斩。
    所以木西,以買手機(jī)的行為方向,進(jìn)行思考随静,可以得出以下結(jié)論:

目標(biāo)接口:買手機(jī)動(dòng)作八千,行為抽象
目標(biāo)對(duì)象:我->買手機(jī)的人->買手機(jī)的行為
代理對(duì)象:代購(gòu)的人->同學(xué)或者朋友->代理買手機(jī)的行為
其他對(duì)象:調(diào)用購(gòu)買手機(jī)的對(duì)象
  • 案例代碼實(shí)現(xiàn): 代碼實(shí)現(xiàn)三步走;

1.將行為抽象挪挤,實(shí)現(xiàn)目標(biāo)接口

 目標(biāo)接口:IShopPhone(購(gòu)物->手機(jī))

2.實(shí)現(xiàn)行為叼丑,完成購(gòu)買動(dòng)作

目標(biāo)對(duì)象:ShopPhone
特點(diǎn):實(shí)現(xiàn)目標(biāo)接口


3.實(shí)現(xiàn)代理對(duì)象,改變由代理對(duì)象完成扛门。

代理對(duì)象:WJProxy
        特點(diǎn)一:實(shí)現(xiàn)目標(biāo)接口(可有可無)
        特點(diǎn)二:持有目標(biāo)對(duì)象的引用(必需)
源碼展示
/**
 * 目標(biāo)接口,抽象購(gòu)買行為
 */
public interface IShopPhone {
    /**
     * 購(gòu)買手機(jī)
     * @param str
     */
    void shopPhone(String str);
}

/**
 * 目標(biāo)接口的實(shí)現(xiàn)類纵寝,實(shí)現(xiàn)具體的行為
 */
public class ShopPhone implements IShopPhone {
    @Override
    public void shopPhone(String str) {
        System.out.println(str);
    }

}

/**
 * 代理類论寨,代理購(gòu)買手機(jī)的行為。
 */
public class ProxyShopPhone implements IShopPhone {
    IShopPhone mTarget;

    /**
     * 目標(biāo)
     */
    public ProxyShopPhone(IShopPhone target) {
        this.mTarget = target;
    }

    @Override
    public void shopPhone(String str) {
        System.out.println("-----start----");
        mTarget.shopPhone(str);
        System.out.println("-----end----");
    }
}

/**
 * 用戶類爽茴,用戶進(jìn)行買手機(jī)的操作
 */
public class User {
    public static void main(String[] args) {
        /*在大陸買手機(jī)*/
        ShopPhone shopPhone = new ShopPhone();
        shopPhone.shopPhone("在大陸買了一個(gè)iPhone X火焰,一共花了8888元");
        /*在香港買手機(jī)*/
        ProxyShopPhone proxyShopPhone = new ProxyShopPhone(shopPhone);
        proxyShopPhone.shopPhone("通過代購(gòu),在香港買了一個(gè)iphone X谦疾,一共花了6666元");
    }
}


————————————————————控制臺(tái)顯示的結(jié)果—————————————————————

在大陸買了一個(gè)iPhone X晚顷,一共花了8888元
-----start----
通過代購(gòu)词爬,在香港買了一個(gè)iphone X恋沃,一共花了6666元
-----end----

Process finished with exit code 0

——————————————END——————————————


動(dòng)態(tài)代理

與靜態(tài)代理的區(qū)別

動(dòng)態(tài)創(chuàng)建代理類(虛擬機(jī)->框架塔橡、系統(tǒng)幫助我們來完成創(chuàng)建過程)

動(dòng)態(tài)代理的特點(diǎn)

1.代理對(duì)象不需要實(shí)現(xiàn)接口。
2.不需要自己實(shí)現(xiàn)代理對(duì)象,由虛擬機(jī)動(dòng)態(tài)生成(內(nèi)部通過java反射實(shí)現(xiàn))双仍。
3.動(dòng)態(tài)代理也叫做JDK代理或接口代理逗物。

代碼實(shí)現(xiàn)

將動(dòng)態(tài)代理中秀睛,不使用靜態(tài)代理中的代理對(duì)象田盈,通過jdk中自帶的代理方法實(shí)現(xiàn)動(dòng)態(tài)代理。


/**
 * 操作類---測(cè)試類
 */
public class Main {
    public static void main(String[] args) {
        /*在大陸買手機(jī)*/
        ShopPhone shopPhone = new ShopPhone();
        shopPhone.shopPhone("在大陸買了一個(gè)iPhone X,一共花了8888元");
        /*動(dòng)態(tài)代理---在香港買手機(jī)*/
        IShopPhone proxy = (IShopPhone) Proxy.newProxyInstance(shopPhone.getClass().getClassLoader(), shopPhone.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("-----動(dòng)態(tài)代理 start----");
                Object returnValue = method.invoke(shopPhone, args);
                System.out.println("-----動(dòng)態(tài)代理 end----");
                return returnValue;
            }
        });
        proxy.shopPhone("通過代購(gòu)肛跌,在香港買了一個(gè)iphone X,一共花了6666元");
    }

  • newProxyInstance詳解:

ClassLoader loader: 指定當(dāng)前目標(biāo)對(duì)象使用類加載器,即目標(biāo)對(duì)象的類加載器眷柔。獲取方法是固定的:[shopPhone.getClass().getClassLoader()]喳坠。

Class<?>[] interfaces: 目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類型,使用泛型方式確認(rèn)類型聋涨。獲取方法是固定的:[shopPhone.getClass().getInterfaces()]茂腥。

InvocationHandler h: 事件處理,執(zhí)行目標(biāo)對(duì)象的方法時(shí),會(huì)觸發(fā)事件處理器的方法,會(huì)把當(dāng)前執(zhí)行目標(biāo)對(duì)象的方法作為參數(shù)傳入般渡。即動(dòng)態(tài)代理類接口的實(shí)現(xiàn)蝴乔,通過JAVA反射實(shí)現(xiàn)。

封裝動(dòng)態(tài)代理工廠代碼實(shí)現(xiàn)


/**
 * 動(dòng)態(tài)代理工廠
 */
public class ProxyFactory {
    private Object mTarget;

    /**
     * 維護(hù)一個(gè)目標(biāo)對(duì)象
     */
    public ProxyFactory(Object target) {
        this.mTarget = target;
    }

    /**
     * 獲取動(dòng)態(tài)代理對(duì)象
     *
     * @return
     */
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(mTarget.getClass().getClassLoader(), mTarget.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("--------start-------");
                Object returnValue = method.invoke(mTarget,args);
                System.out.println("---------end--------");
                return returnValue;
            }
        });
    }
}


——————————————————————使用方法—————————————————————
 proxy.shopPhone("通過代購(gòu),在香港買了一個(gè)iphone X,一共花了6666元");
        IShopPhone proxy2 = (IShopPhone) new ProxyFactory(shopPhone).getProxyInstance();
        proxy2.shopPhone("通過代購(gòu)商店霞捡,在香港買了一個(gè)iphone X街夭,一共花了6666元");

——————————————————————控制臺(tái)輸出結(jié)果—————————————————————
在大陸買了一個(gè)iPhone X,一共花了8888元
--------start-------
通過代購(gòu)商店埃碱,在香港買了一個(gè)iphone X乃正,一共花了6666元
---------end--------

Process finished with exit code 0

cglib動(dòng)態(tài)代理

基本概述

可繼承式的動(dòng)態(tài)代理(java僅允許單繼承凡人,而JDK中的代理類挠轴,自身就會(huì)繼承Proxy)启上,在android開發(fā)中包券,一般不會(huì)使用付秕。
使用該方式實(shí)現(xiàn)動(dòng)態(tài)代理嵌削,需要倒入cglib.jar艇劫,或使用spring框架吼驶,即該方式的動(dòng)態(tài)代理,大多在后臺(tái)服務(wù)器中使用店煞,在本片文章中不會(huì)過多講解蟹演;

代碼實(shí)現(xiàn)

/**

/**
 * 通過繼承實(shí)現(xiàn)的動(dòng)態(tài)代理類
 */
public class CGLibProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();
    public Object getProxy(Class<?> clazz){
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    /**
     * 攔截所有目標(biāo)類方法的調(diào)用
     * 參數(shù):
     * obj目標(biāo)實(shí)例對(duì)象
     *method 目標(biāo)方法的反射對(duì)象
     * args方法的參數(shù)
     * proxy代理類的實(shí)例
     */
    public Object intercept(Object obj, Method method, Object[] args,
                            MethodProxy proxy) throws Throwable {
        //代理類調(diào)用父類的方法
        System.out.println("日志開始");
        proxy.invokeSuper(obj, args);
        System.out.println("日志結(jié)束");
        return null;
    }
}
——————————————————————使用方法—————————————————————
 /*通過cglib.jar實(shí)現(xiàn)可繼承的動(dòng)態(tài)代理*/
    IShopPhone proxy3 = (IShopPhone) new CGLibProxy().getProxy(ShopPhone.class);
    proxy3.shopPhone("通過代購(gòu),在香港買了一個(gè)iphone X顷蟀,一共花了6666元");
       

使用場(chǎng)景

例如:開發(fā)當(dāng)中->類似框架(代理模式)
    XUtils框架酒请、Retrofit框架、MVP架構(gòu)設(shè)計(jì)鸣个、插件化架構(gòu)設(shè)計(jì)等等...

最后羞反,為自己留一個(gè)作業(yè):如何通過java反射自己實(shí)現(xiàn)JDK動(dòng)態(tài)代理?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末囤萤,一起剝皮案震驚了整個(gè)濱河市昼窗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌阁将,老刑警劉巖膏秫,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異做盅,居然都是意外死亡缤削,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門吹榴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來亭敢,“玉大人,你說我怎么就攤上這事图筹∷У叮” “怎么了让腹?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)扣溺。 經(jīng)常有香客問我骇窍,道長(zhǎng),這世上最難降的妖魔是什么锥余? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任腹纳,我火速辦了婚禮,結(jié)果婚禮上驱犹,老公的妹妹穿的比我還像新娘嘲恍。我一直安慰自己,他們只是感情好雄驹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布佃牛。 她就那樣靜靜地躺著,像睡著了一般医舆。 火紅的嫁衣襯著肌膚如雪俘侠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天彬向,我揣著相機(jī)與錄音兼贡,去河邊找鬼。 笑死娃胆,一個(gè)胖子當(dāng)著我的面吹牛遍希,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播里烦,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼凿蒜,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了胁黑?” 一聲冷哼從身側(cè)響起废封,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎丧蘸,沒想到半個(gè)月后漂洋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡力喷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年刽漂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弟孟。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贝咙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拂募,到底是詐尸還是另有隱情庭猩,我是刑警寧澤窟她,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站蔼水,受9級(jí)特大地震影響震糖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜徙缴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一试伙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧于样,春花似錦、人聲如沸潘靖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卦溢。三九已至糊余,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間单寂,已是汗流浹背贬芥。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宣决,地道東北人蘸劈。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像尊沸,于是被迫代替她去往敵國(guó)和親威沫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353