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

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

    遠程代理

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

    第一步:定義遠程接口

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

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

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

    第二步:實現遠程服務

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

    2.繼承UnicastRemoteObject?
    要想成為一個遠程服務對象,需要有遠程的功能。最簡單的方法就是實現UnicastRemoteObject方法了。

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

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

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

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

    第四步:運行rmiregistry

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

    第五步:啟動服務

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


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

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


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

    在RMI中,找到服務后,拿到的MyRemote service其實是一個代理對象(MyRemote_Stub),對代理對象的方法調用,實際是通過RMI來訪問遠程服務實現對象的方法。也就是說代理對象MyRemote service(實際是MyRemote_Stub)通過RMI機制對遠程服務對象來做訪問控制,也就實現了代理模式。

    虛擬代理

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

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


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


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


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

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

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


    JDK動態代理

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

    使用場合:

    好像在在一本Hibernate的書上,對數據庫Connection的close方法調用,用動態代理的方式來攔截,并不真正關閉連接,而是返回到數據庫連接池中。

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

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

    PersonBean為Subject接口,OwnerInvocationHandler實現InvocationHandler接口。

    和Decorator的比較

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


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


    網站導航:
     

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

    Copyright © Lv Yuanfang

    主站蜘蛛池模板: 亚洲好看的理论片电影| 亚洲?V无码成人精品区日韩| 久久精品国产96精品亚洲| 国产vA免费精品高清在线观看| 日韩精品亚洲专区在线观看| 羞羞漫画页面免费入口欢迎你| 又爽又黄无遮挡高清免费视频 | 国产成人亚洲精品| 日韩版码免费福利视频| 亚洲国产精品网站在线播放| 日韩在线视频免费看| 特级aa**毛片免费观看| 中文字幕亚洲一区| 伊人久久免费视频| 亚洲精品中文字幕无乱码麻豆 | 亚洲国产精品视频| fc2免费人成为视频| 久久亚洲综合色一区二区三区| 84pao强力永久免费高清| 亚洲av无码片在线观看| 午夜视频在线观看免费完整版| 色网站在线免费观看| 亚洲国产精品VA在线观看麻豆| 亚洲美女免费视频| 亚洲AV永久无码精品网站在线观看| 免费人成视频在线观看不卡| 成人免费区一区二区三区| 亚洲人配人种jizz| 久久久精品国产亚洲成人满18免费网站| 中国国产高清免费av片| 亚洲日本在线播放| 全部免费毛片免费播放| 四虎国产精品永久免费网址 | 亚洲色偷偷狠狠综合网| 免费国产在线视频| 亚洲AV无码无限在线观看不卡 | 2019中文字幕在线电影免费 | 亚洲最大成人网色| 日韩免费视频一区| 午夜免费福利小电影| 亚洲av无一区二区三区|