<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Head First Pattern之代理模式

    Posted on 2008-09-13 23:48 Lv Yuanfang 閱讀(619) 評論(0)  編輯  收藏

    Head First Pattern之代理模式


    去年買的Head First Pattern英文版,看了一點(diǎn)點(diǎn),看起來還是比較吃力。。今年開始一點(diǎn)點(diǎn)的看,慢慢的看進(jìn)去了,真是好書啊,一點(diǎn)點(diǎn)的從實(shí)際例子入手,一步步的、循序漸進(jìn)的說明每一個設(shè)計模式,真是足夠的深入淺出!以前也看過閻宏的《Java與模式》,結(jié)合中國的傳統(tǒng)道家文化、儒家思想,甚至西游記、紅樓夢、女媧造人都用上了,說的是也算夠透徹了的,但是總感覺還是有些東西理解的不太深。
    下面總結(jié)下這些天看的代理模式。。

    一句話概括代理模式,就是用代理對象對真實(shí)對象的訪問控制,代理對象和真實(shí)對象都實(shí)現(xiàn)同一個Subject接口。
    類圖表示如下:
    圖截自http://refcardz.dzone.com/里的免費(fèi)書:Design Patterns
    個人理解,代理模式在現(xiàn)實(shí)例子里,可以有非常多的變種,關(guān)鍵在于代理對象如何實(shí)現(xiàn)對真實(shí)對象的訪問控制。變化在于訪問控制的方式。著重說明下書中的3個例子,就是3種代理模式的使用場合。。

    遠(yuǎn)程代理

    遠(yuǎn)程代理的例子是java中的RMI。真是足夠深入淺出的,讓我以前對RMI非常模糊的印象也漸漸清晰起來。咱們一步步細(xì)細(xì)道來。。

    第一步:定義遠(yuǎn)程接口

    1.繼承java.rmi.Remote接口
    定義服務(wù)接口,服務(wù)接口必須繼承自Remote接口。Remote接口是一個標(biāo)記接口,就是這個接口,沒有任何要實(shí)現(xiàn)的方法,僅僅是用來標(biāo)識其實(shí)現(xiàn)類具有某種功能(個人理解),就像Serializable接口,僅僅表示實(shí)現(xiàn)這個接口的類能被序列化。
    public interface MyRemote extends Remote {

    2.服務(wù)接口中所有方法拋出RemoteException異常
    RMI客戶端的方法調(diào)用其實(shí)是調(diào)用實(shí)現(xiàn)Remote接口的Stub(樁),樁的實(shí)現(xiàn)是基于網(wǎng)絡(luò)和IO的(底層就是socket),客戶端在調(diào)用方法過程中,任何錯誤都有可能發(fā)生,所以必須讓客戶端知道所發(fā)生的異常,并能捕捉。
    import java.rmi.*;
    public interface MyRemote extends Remote {
    ?? ?public String sayHello() throws RemoteException;
    }

    3.保證返回值和參數(shù)必須是可序列化的
    遠(yuǎn)程方法的參數(shù)要通過網(wǎng)絡(luò)傳輸,因此必須是可序列化的,返回值也是同樣。如果用原生類型(int、float等)、String、集合等,就沒問題,如果用自己的類型,必須實(shí)現(xiàn)Serializable接口(和Remote接口一樣,都是標(biāo)記接口)。

    第二步:實(shí)現(xiàn)遠(yuǎn)程服務(wù)

    1.實(shí)現(xiàn)遠(yuǎn)程接口
    public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{
    ?? ?public String sayHello(){
    ?? ??? ?return "Hello, I'm server.";
    ?? ?}
    }

    2.繼承UnicastRemoteObject?
    要想成為一個遠(yuǎn)程服務(wù)對象,需要有遠(yuǎn)程的功能。最簡單的方法就是實(shí)現(xiàn)UnicastRemoteObject方法了。

    3.聲明一個無參數(shù)的構(gòu)造函數(shù),且拋出RemoteException
    public MyRemoteImpl() throws RemoteException{}

    4.用RMI registry注冊服務(wù)
    實(shí)現(xiàn)遠(yuǎn)程服務(wù)后,要布遠(yuǎn)程服務(wù)供客戶端使用。要實(shí)例化一個遠(yuǎn)程服務(wù),放入RMI注冊表中。注冊了服務(wù)實(shí)現(xiàn)對象后,RMI會把Stub(樁)放入注冊表,讓客戶端使用。
    try{
    ?? ?MyRemote service = new MyRemoteImpl();
    ?? ?Naming.rebind("RemoteHello",service);
    }catch(Exception e){
    ?? ?// ...
    }

    第三步:生成Stub和Skeletons(樁和骨架)

    1.在遠(yuǎn)程實(shí)現(xiàn)類上運(yùn)行rmic(不是遠(yuǎn)程接口)
    rmic MyRemoteImpl(類名,不帶.class)
    會生成樁和骨架代碼:MyRemoteImpl_Stub.class、MyRemoteImpl_Skel.class
    rmic是jdk bin目錄下的工具

    第四步:運(yùn)行rmiregistry

    1.rmiregistry
    必須讓rmiregistry能訪問到你的服務(wù)相關(guān)類,要么把類放入classpath,要么在classes目錄下直接運(yùn)行rmiregistry

    第五步:啟動服務(wù)

    1.另一個dos窗口里啟動服務(wù)類
    java MyRemoteImpl


    客戶端調(diào)用方法:
    MyRemote service = (MyRemote)Naming.lookup("rmi://127.0.0.1/RemoteHello");
    String msg = service.sayHello();// 調(diào)用樁的方法

    通過RMI registry查找服務(wù)后,返回樁,客戶端必須用MyRemoteImpl_Stub.class和MyRemote.class。樁MyRemoteImpl_Stub.class、骨架MyRemoteImpl_Skel.class、MyRemote.class、MyRemoteImpl.class必須在服務(wù)端。


    這么多亂七八糟的跟代理模式有什么關(guān)系?
    其實(shí)客戶端返回的MyRemote,其實(shí)是MyRemote_Stub,就是代理對象了,服務(wù)端的MyRemoteImpl及時實(shí)際對象,通過RMI,來獲得遠(yuǎn)程對象的代理,再通過代理,來訪問實(shí)際對象(遠(yuǎn)程服務(wù)實(shí)現(xiàn)類MyRemoteImpl)所實(shí)現(xiàn)的遠(yuǎn)程服務(wù)方法(MyRemote定義)。對應(yīng)類圖,每個類在代理模式中的角色分別是:
    Subject:MyRemote接口
    RealObject:MyRemoteImpl服務(wù)實(shí)現(xiàn)類
    Proxy:MyRemote_Stub樁

    在RMI中,找到服務(wù)后,拿到的MyRemote service其實(shí)是一個代理對象(MyRemote_Stub),對代理對象的方法調(diào)用,實(shí)際是通過RMI來訪問遠(yuǎn)程服務(wù)實(shí)現(xiàn)對象的方法。也就是說代理對象MyRemote service(實(shí)際是MyRemote_Stub)通過RMI機(jī)制對遠(yuǎn)程服務(wù)對象來做訪問控制,也就實(shí)現(xiàn)了代理模式。

    虛擬代理

    虛擬代理舉的是一個Swing的例子。

    我是這么理解的:一個對象的創(chuàng)建非常耗時,通過代理對象去調(diào)用,在真實(shí)對象創(chuàng)建前,返回一個假的調(diào)用,等真實(shí)對象創(chuàng)建好了,這時候返回給客戶端的就是一個真實(shí)對象的相應(yīng)方法調(diào)用。


    也就是延遲加載的問題,Swing例子中,要顯示一個Icon,但是要通過網(wǎng)絡(luò)加載一個圖片,在圖片通過網(wǎng)絡(luò)加載成功前,先顯示一個“加載中,請稍候...”(如果是真實(shí)對象的調(diào)用,應(yīng)該顯示一個圖片),在代理對象中通過后臺線程去加載圖片,加載完了后,再偷偷的把“加載中,請稍候...”的字樣偷偷換成加載成功后的圖片。


    沒想到這也算代理模式的一種應(yīng)用場景。以前有這么在Swing中用過,需要從數(shù)據(jù)庫中查找數(shù)據(jù),但是比較耗時,就先顯示“加載數(shù)據(jù)中,請稍候...”,等加載完了,再在JTable中顯示出來。如果用代理模式的方式來思考,好像比較的好吧。。


    同樣在jsp頁面里,通過ajax來加載數(shù)據(jù)好像也是這樣的道理,數(shù)據(jù)沒加載之前就是“加載中...”,加載完了再通過innerHTML來改變顯示,也是同樣的延遲加載問題。

    如果用代理模式的方式來考慮,可以定義一個JavaScript類(這個類其實(shí)是個代理),這個類有個方法要顯示一些從Server取出的數(shù)據(jù),但是調(diào)用顯示方法時,后臺數(shù)據(jù)還沒有加載,就先顯示加載中請稍候之類的文本,這時候通過ajax從Server取數(shù)據(jù)(創(chuàng)建真實(shí)對象),取出來之后在回調(diào)函數(shù)中更新顯示HTML元素的innerHTML。跟那個Swing的例子一模一樣吧。不過好像JavaScript中好像沒有誰會定義接口、實(shí)現(xiàn)、代理對象吧,但是思路其實(shí)是一樣的。

    不知道這樣理解代理模式,算不算曲解。。。


    JDK動態(tài)代理

    jdk里的動態(tài)代理支持,主要是通過java.lang.reflect包中Proxy、InvocationHandler等幾個類來實(shí)現(xiàn)的。具體如何實(shí)現(xiàn)可參考JDK中文文檔。

    使用場合:

    好像在在一本Hibernate的書上,對數(shù)據(jù)庫Connection的close方法調(diào)用,用動態(tài)代理的方式來攔截,并不真正關(guān)閉連接,而是返回到數(shù)據(jù)庫連接池中。

    在Spring中的攔截貌似有些是用動態(tài)代理實(shí)現(xiàn)的?不過動態(tài)代理使用時要基于接口,但是Spring是使用動態(tài)生成字節(jié)碼的方式?對Spring內(nèi)部實(shí)現(xiàn)機(jī)制不熟。。不敢妄自猜測。。等有時間好好研究再來說明。。

    動態(tài)代理,我覺得最好的使用場合是給方法調(diào)用增加預(yù)處理和后處理,更加靈活了,可以做一些額外的事,同時也做到無侵入的解耦合,因?yàn)榇韺ο蠛蛯?shí)際對象的接口是一樣的,唯一需要注意的地方是,客戶端調(diào)用者是拿的接口,接口到底是使用代理對象還是實(shí)際對象,調(diào)用者并不知道,這就需要對代理對象的創(chuàng)建用類似工廠的方式來封裝創(chuàng)建。比如一下代碼:
    PersonBean getOwnerProxy(PersonBean person){
    ?? ?return (PersonBean)Proxy.newProxyInstance(
    ?? ??? ? ? ? person.getClass().getClassLoader(),
    ?? ??? ??? ? person.getClass().getInterfaces(),
    ?? ??? ??? ? new OwnerInvocationHandler(person));
    }

    PersonBean為Subject接口,OwnerInvocationHandler實(shí)現(xiàn)InvocationHandler接口。

    和Decorator的比較

    Decorator模式在jdk的java.io包中使用非常廣泛。主要用來為一個類添加新的行為。
    而Proxy模式中,代理對象并不對實(shí)際對象添加新的行為,只是對實(shí)際對象做訪問控制。


    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     

    posts - 11, comments - 2, trackbacks - 0, articles - 0

    Copyright © Lv Yuanfang

    主站蜘蛛池模板: 免费人成无码大片在线观看| 四虎亚洲精品高清在线观看| 一区二区三区免费在线视频 | 国产一级一片免费播放i| 亚洲国产精品综合久久网各| 男人都懂www深夜免费网站| 亚洲精品二区国产综合野狼| 国产成人免费ā片在线观看老同学| 亚洲中久无码永久在线观看同| 人妻免费久久久久久久了| 区久久AAA片69亚洲| a级毛片黄免费a级毛片| 亚洲av无码一区二区三区乱子伦 | 色婷五月综激情亚洲综合| 97在线观免费视频观看| 亚洲精品国产第一综合99久久| 拔擦拔擦8x华人免费久久| 麻豆va在线精品免费播放| 国产亚洲情侣一区二区无| 国产在线精品一区免费香蕉 | 好爽…又高潮了免费毛片| 亚洲AV无码一区二区三区久久精品 | 国产又黄又爽又猛的免费视频播放| 色屁屁www影院免费观看视频| 精品亚洲一区二区三区在线播放| 女人隐私秘视频黄www免费| 亚洲精品亚洲人成在线麻豆| 两个人的视频高清在线观看免费| 亚洲AV无码一区二区三区电影| 日韩一卡2卡3卡4卡新区亚洲| 精品无码人妻一区二区免费蜜桃| 国产午夜亚洲精品国产| 免费播放特黄特色毛片| 国产午夜精品免费一区二区三区| 亚洲国产精品综合久久久| 免费国产人做人视频在线观看| 中文字幕免费观看全部电影| 亚洲午夜一区二区三区| 亚洲午夜福利精品久久| 亚洲精品免费网站| 国产va免费观看|