摘要:名字看著有點(diǎn)繞但目的其實(shí)很簡(jiǎn)單明確就是想實(shí)現(xiàn)動(dòng)態(tài)代理的對(duì)象實(shí)例在運(yùn)行時(shí)也能夠切換先理解前提條件和程序上下文譬如有如下接口我們將接口的一個(gè)實(shí)例傳入了一個(gè)別的類或者外部在運(yùn)行時(shí)我們生成了不同的實(shí)例現(xiàn)在希望能夠替換但對(duì)于類是無(wú)法感知不用關(guān)心的顯然
名字看著有點(diǎn)繞, 但目的其實(shí)很簡(jiǎn)單明確: 就是想實(shí)現(xiàn)動(dòng)態(tài)代理的對(duì)象實(shí)例, 在運(yùn)行時(shí)也能夠切換.
先理解前提條件和程序上下文, 譬如有如下接口:
public interface Responder { void onMethod1(String s); int onMethod2(); void onMethod3(); }
我們將接口的一個(gè)實(shí)例Responder r1傳入了一個(gè)別的類p1 = new Presenter(r1)(或者外部SDK), 在運(yùn)行時(shí)我們生成了不同的Responder實(shí)例r2, 現(xiàn)在希望r2能夠替換r1, 但對(duì)于Presenter類是無(wú)法感知, 不用關(guān)心的. 顯然我們的程序上下文能夠?qū)崿F(xiàn)對(duì)于Responder實(shí)例的控制(創(chuàng)建/傳遞), 但現(xiàn)在問題是Presenter類僅有構(gòu)造參數(shù)對(duì)Responder的傳入, 沒有setResponder(Responder r)這樣的方法(如果存在setResponder這樣的方法, 但就沒這坨事了:). 能不能再創(chuàng)建一個(gè)Presenter實(shí)例p2再傳入r2呢? 如果程序上下文允許的話也沒這坨事了.
所以條件是這樣: 接口的不同實(shí)例需要傳入一個(gè)對(duì)象, 但這個(gè)對(duì)象持有的實(shí)例卻無(wú)法更改, 同時(shí)這個(gè)對(duì)象也無(wú)法再次創(chuàng)建.
說這么多不就是要用代理模式嗎? 不錯(cuò), 代理模式正是可以解決這類問題的. 表述這么累贅是想關(guān)注問題的場(chǎng)景, 而不是為了生搬硬套模式.
于是一個(gè)簡(jiǎn)單的代理類出來了:
public class ResponderWrapper implements Responder { private final Responder impl; public ResponderWrapper(Responder r) { impl = r; } @Override void onMethod1(String s) { impl.onMethod1(s); } @Override int onMethod2() { return impl.onMethod2(); } @Override void onMethod3() { impl.onMethod3(); } }
因?yàn)檫€要?jiǎng)討B(tài)的改變代理對(duì)象所以添加一個(gè)set方法:
void setResponder(Responder r) { impl = r; }
那么傳入Presenter對(duì)象的實(shí)例就不再是r1了, 而是
wrapper = new ResponderWrapper(r1); p1 = new Presenter(wrapper);
這時(shí)創(chuàng)建了新的Responder實(shí)例r2, 我們只需要
wrapper.setResponder(r2);
就能夠達(dá)到我們的目的了! p1還是p1, p1持有的實(shí)例還是同一個(gè)實(shí)例, 在切換前p1調(diào)的是r1的實(shí)現(xiàn), 切換后自然就調(diào)用了r2的實(shí)現(xiàn).
這種代理就是非常常見的靜態(tài)代理, 僅就功能實(shí)現(xiàn)來說這已經(jīng)完全OK了, 沒有任何問題了. 是不是非得用動(dòng)態(tài)代理? 并不是!
那動(dòng)態(tài)代理是干嗎的? 為了適應(yīng)變化, 什么的變化? 接口的變化! 如果接口Responder新增一個(gè)方法, ResponderWrapper再增加同樣一個(gè)接口; 如果修改Responder一個(gè)方法的參數(shù), ResponderWrapper再接著修改并調(diào)用接口實(shí)例的新方法, 如此類推, 也沒任何問題. 但接口的方法一旦變的很多, 接口的實(shí)現(xiàn)類一旦變的很多, 就需要做大量繁瑣重復(fù)的工作, 那么動(dòng)態(tài)代理就能夠解決這種重復(fù)繁瑣的工作.
以動(dòng)態(tài)代理的形式寫一個(gè)ResponderWrapper非常簡(jiǎn)單:
public final class ResponderWrapper { public static Responder wrap(final Responder responder) { return (Responder) Proxy.newProxyInstance(Responder.class.getClassLoader(), Responder.class.getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(responder, args); } }); } }
但是這樣寫無(wú)法滿足動(dòng)態(tài)切換的需求, 所以我們的最終目的這才出來了: 以動(dòng)態(tài)代理形式創(chuàng)建的代理實(shí)例能夠動(dòng)態(tài)切換持有的對(duì)象實(shí)例
但一旦ResponderWrapper.wrap傳入r1那么匿名對(duì)象持有的Responder對(duì)象就只能一直是r1, 所以希望method.invoke(responder, args)這里的responder能夠動(dòng)態(tài)切換, 這種"動(dòng)態(tài)"能力一般都是以接口的形式實(shí)現(xiàn), 于是有:
public final class ResponderWrapper { public interface Provider { Responder get(); } public static Responder wrap(final Provider provider) { return (Responder) Proxy.newProxyInstance(Responder.class.getClassLoader(), Responder.class.getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(provider.get(), args); } }); }
程序上下文實(shí)現(xiàn)ResponderWrapper.Provider接口, 當(dāng)接口方法被調(diào)用時(shí)返回的實(shí)例是當(dāng)前的Responder, 不用關(guān)心什么時(shí)候切換:
mResonder = r1; wrapper = ResponderWrapper.wrap(new ResponderWrapper.Provider() { @Override public ResponderWrapper.Responder get() { return mResponder; } }); p1 = new Presenter(wrapper); ... mResonder = r2;
如果覺得接口太重, 其實(shí)這種形式也完全可以不用接口的方式實(shí)現(xiàn), 因?yàn)槲覀冏罱K需要的其實(shí)是一個(gè)Responder實(shí)例, 在接口方法被調(diào)用的時(shí)候能夠調(diào)用這個(gè)實(shí)例的對(duì)應(yīng)的方法而已, 所以可以寫成這樣:
public final class ResponderWrapper { public static final class Holder { public Responder responder; } public static Responder wrap(final Holder holder) { return (Responder) Proxy.newProxyInstance(Responder.class.getClassLoader(), Responder.class.getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(holder.responder, args); } }); } }
程序上下文持有ResponderWrapper.Holder的實(shí)例, 再在需要的時(shí)候設(shè)置不同的Resonder實(shí)例:
mHolder = new ResponderWrapper.Holder(r1); wrapper = ResponderWrapper.wrap(holder) p1 = new Presenter(wrapper); ... mHolder.responder = r2
如果用范型抽象所有接口類, 就可以寫的更通用一點(diǎn):
public final class ResponderWrapper { public static final class Holder{ public T responder; } @SuppressWarnings("unchecked") public static T wrap(final Holder holder) { T r = holder.responder; return (T) Proxy.newProxyInstance(r.getClass().getClassLoader(), r.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(holder.responder, args); } }); } }
這里臨時(shí)利用holder.responder來獲取ClassLoader和Class>[], 也完全可以將Class對(duì)象傳入:
public final class ResponderWrapper { public static final class Holder{ public T responder; } @SuppressWarnings("unchecked") public static T wrap(final Holder holder, final Class clazz) { return (T) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(holder.responder, args); } }); } }
這就是我們所謂的動(dòng)態(tài)切換的動(dòng)態(tài)代理了.
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/74775.html
摘要:簡(jiǎn)單工廠模式的實(shí)質(zhì)是由一個(gè)工廠類根據(jù)傳入的參數(shù),動(dòng)態(tài)決定應(yīng)該創(chuàng)建哪一個(gè)產(chǎn)品類。中的就是簡(jiǎn)單工廠模式的體現(xiàn),根據(jù)傳入一個(gè)唯一的標(biāo)識(shí)來獲得對(duì)象,但是否是在傳入?yún)?shù)后創(chuàng)建還是傳入?yún)?shù)前創(chuàng)建這個(gè)要根據(jù)具體情況來定。 設(shè)計(jì)模式作為工作學(xué)習(xí)中的枕邊書,卻時(shí)常處于勤說不用的尷尬境地,也不是我們時(shí)常忘記,只是一直沒有記憶。 Spring作為業(yè)界的經(jīng)典框架,無(wú)論是在架構(gòu)設(shè)計(jì)方面,還是在代碼編寫方面,都堪...
摘要:簡(jiǎn)單工廠模式的實(shí)質(zhì)是由一個(gè)工廠類根據(jù)傳入的參數(shù),動(dòng)態(tài)決定應(yīng)該創(chuàng)建哪一個(gè)產(chǎn)品類。中的就是簡(jiǎn)單工廠模式的體現(xiàn),根據(jù)傳入一個(gè)唯一的標(biāo)識(shí)來獲得對(duì)象,但是否是在傳入?yún)?shù)后創(chuàng)建還是傳入?yún)?shù)前創(chuàng)建這個(gè)要根據(jù)具體情況來定。 設(shè)計(jì)模式作為工作學(xué)習(xí)中的枕邊書,卻時(shí)常處于勤說不用的尷尬境地,也不是我們時(shí)常忘記,只是一直沒有記憶。 Spring作為業(yè)界的經(jīng)典框架,無(wú)論是在架構(gòu)設(shè)計(jì)方面,還是在代碼編寫方面,都堪...
摘要:如問到是否使用某框架,實(shí)際是是問該框架的使用場(chǎng)景,有什么特點(diǎn),和同類可框架對(duì)比一系列的問題。這兩個(gè)方向的區(qū)分點(diǎn)在于工作方向的側(cè)重點(diǎn)不同。 [TOC] 這是一份來自嗶哩嗶哩的Java面試Java面試 32個(gè)核心必考點(diǎn)完全解析(完) 課程預(yù)習(xí) 1.1 課程內(nèi)容分為三個(gè)模塊 基礎(chǔ)模塊: 技術(shù)崗位與面試 計(jì)算機(jī)基礎(chǔ) JVM原理 多線程 設(shè)計(jì)模式 數(shù)據(jù)結(jié)構(gòu)與算法 應(yīng)用模塊: 常用工具集 ...
摘要:看下圖所示,摘自網(wǎng)絡(luò)的創(chuàng)建流程源碼分析實(shí)例是使用建造者模式通過類進(jìn)行創(chuàng)建的。創(chuàng)建了一個(gè)含有對(duì)象實(shí)例的,并返回給源碼分析添加一個(gè)調(diào)用適配器工廠,用于支持服務(wù)方法返回類型注意生產(chǎn)的是,那么又是什么呢可以看到源代碼如下所示,它是一個(gè)接口。 目錄介紹 1.首先回顧Retrofit簡(jiǎn)單使用方法 2.Retrofit的創(chuàng)建流程源碼分析 2.1 Retrofit對(duì)象調(diào)用Builder()源碼解...
閱讀 3127·2021-09-28 09:42
閱讀 3461·2021-09-22 15:21
閱讀 1133·2021-07-29 13:50
閱讀 3586·2019-08-30 15:56
閱讀 3377·2019-08-30 15:54
閱讀 1204·2019-08-30 13:12
閱讀 1185·2019-08-29 17:03
閱讀 1207·2019-08-29 10:59