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

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

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

    hengheng123456789

      BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
      297 Posts :: 68 Stories :: 144 Comments :: 0 Trackbacks
     

    Observer Pattern Beginning

     

    http://www.levilee.cn/Jeans/304/Play_9426_1/

     

    1.     使用觀察者(Observer)實現(xiàn)對象監(jiān)聽

    觀察者(Observer)是一種模式,也是Java中的一個API,它讓一個值對象(Value Object)具備自省的功能,當他發(fā)現(xiàn)自己的狀態(tài)改變了,就向相關的對象發(fā)送消息,這樣的監(jiān)聽方式當然比輪詢好。我感冒了自己會去醫(yī)院,用不著醫(yī)生每個月來問一次。

    JavaObserver API是對觀察者模式的一個實現(xiàn)。假設我們有一個對象容器,其中存放用戶消息,我希望這個容器自省,當有新的消息進來就自動觸發(fā)觀察者作出響應。首先定義消息對象,是個很簡單的值對象:

    package com.gwnet.smsMessenger.mm.bromon;

    public class Message

    {

    private int id;

    private String sender;

    private String receiver;

    private String content;

    private String time;

    //請自己實現(xiàn)set/get方法,比如:

    public int getId()

    {

    return id;

    }

    public void setId(int id)

    {

    this.id=id;

    }

    }

    然后寫一個存放Message的容器,容器使用ArrayList來存放對象是個很好的選擇,也很簡單:

    package com.gwnet.smsMessenger.mm.bromon;

    import java.util.*;

    public class MessageList extends Observable

    {

        private List m=new ArrayList();

        private static MessageList ml=null;

        

        public MessageList()

        {

        }

        

        public static MessageList getInstance()

        {

            if(ml==null)

           {

               ml=new MessageList();

           }

            return ml;

        }

        

        public void add(Message msg)

        {

            m.add(msg);

            super.setChanged();

            super.notifyObservers(m);

        }

        

        public void del(Message msg)

        {

            m.remove(msg);

        }

    }

    這個類繼承了Observable類,并且對其中的add方法做了手腳,很明顯,add方法的作用是向ArrayList容器中放入一個對象,這正是我們想監(jiān)聽的操作,所以有了:

    uper.setChanged();

    super.notifyObservers(m);

    這意思是一旦調(diào)用add方法,這個類自己就會向所有注冊過的觀察者發(fā)送消息,消息內(nèi)容是什么呢?內(nèi)容就是m,是存放消息的容器,觀察者可以收到這個改變了狀態(tài)的容器,然后對它進行操作,從而實現(xiàn)了對容器的監(jiān)聽,當然,我們只實現(xiàn)了對add方法的監(jiān)聽,你也可以試試其他的。

    需要特別注意的是這是一個不完整的單例類,寫成單例是為了要保證整個jvm中只有這一個存放消息的容器,而不寫成完整的單例,原因是將來可能要提供另外的實例化方法。所以理解起來可能稍微難一點,大家可以參考一下設計模式中的單例模式。

    下面就是編寫觀察者并且注冊它:

    package com.gwnet.smsMessenger.bromon;

    import java.util.*;

    public class MessageObserver implements Observer

    {

        public void update(Observable arg0, Object arg1)

        {

           List l=(List)arg1;

            Message m=(Message)l.get(l.size()-1);

            String receiver=m.getReceiver();

            System.out.println(""+m.getReceiver()+”的新消息:”+m.getContent());

        }

    }

    這個類繼承Oberver接口,update(Observable,Object)是必須提供的方法,在這個方法中我們接收被觀察類傳過來的數(shù)據(jù)(含有消息的容器),然后取出其中最后一個,讀取它的內(nèi)容。

    Java里的觀察者使用起來是非常簡單的。我們的例子好處是所有的操作都在內(nèi)存中進行,而且不需要輪詢,效率非常高,缺點是一旦當機內(nèi)存中的數(shù)據(jù)就丟失了,所以如果有一套比較完善的對象緩沖機制,就可以應付復雜的應用,寫出高效簡潔的多線程服務器。

     

     

    http://www.cnblogs.com/goodcandle/archive/2006/04/05/observerext.html

     

    2.        Observer模式為何要區(qū)分推拉模式

           先來比較兩張UML圖:

          

    推模式

    拉模式

           兩者的區(qū)別我再羅嗦一下,推模式是當通知消息來之時,把所有相關信息都通過參數(shù)的形式推給觀察者。而拉模式是當通知消息來之時,通知的函數(shù)不帶任何相關的信息,而是要觀察者主動去信息。

           推模式的優(yōu)點

    是當消息來臨時,觀察者很直接地都到信息,然后進行相關地處理,與被觀察者沒有一點聯(lián)系,兩者幾乎沒有耦合。

           推模式的缺點:

    是當消息來臨時,所有的信息都強迫觀察者,不管有用與否。還有一個致命的缺點是,如果想在通知消息中添加一個參數(shù),那么所有的觀察者都需要修改了,這一點往往被忽視。

           看來事物都有其兩面性一點都不假,信息太全也不是一件好事。

           存在即有理由,為了彌補推模式的不足,拉模式就誕生了。

           就接著上面的例子,如果CPerson2想要都到秒的信息,按推模式來說,CPerson1也就需要修改了,然而用拉模式,各個觀測者之間就沒有什么聯(lián)系了,因為具體的信息還要觀測者主動去,而一旦有了主動權(quán),各個觀察者想拉什么信息就取決于具體的觀察者了,這樣CPerson1就無需修改了,只要在CNotifyBase中再添加一個接口函數(shù)就行了(GetSecond)。

           QCClockDevice不是還要修改嗎?

           A修改是難免的,使用設計模式的目的不是不允許修改,而是讓軟件更易擴展,更易擴展體現(xiàn)在哪里呢?那就是讓修改處盡可能的減少。看到UML圖中那1*了嗎?你現(xiàn)在應該明白了吧?被觀察者只要一個,而且不太會更改,而觀察者確有很多。讓你選擇,你會選擇修改什么呢?

           當然拉模式的缺點也是存在的,那就是:

    和被觀察者有一定的耦合,但我們可以通過接口,把耦合降到最低。

     

    下面是觀察者模式其它一些優(yōu)缺點:

    1 ) 目標和觀察者間的抽象耦合一個目標所知道的僅僅是它有一系列觀察者, 每個都符合抽象的O b s e r v e r類的簡單接口。目標不知道任何一個觀察者屬于哪一個具體的類。這樣目標和觀察者之間的耦合是抽象的和最小的。

    因為目標和觀察者不是緊密耦合的, 它們可以屬于一個系統(tǒng)中的不同抽象層次。一個處于較低層次的目標對象可與一個處于較高層次的觀察者通信并通知它, 這樣就保持了系統(tǒng)層次的完整。如果目標和觀察者混在一塊, 那么得到的對象要么橫貫兩個層次(違反了層次性), 要么必須放在這兩層的某一層中(這可能會損害層次抽象)。

    2) 支持廣播通信不像通常的請求, 目標發(fā)送的通知不需指定它的接收者。通知被自動廣播給所有已向該目標對象登記的有關對象。目標對象并不關心到底有多少對象對自己感興趣;它唯一的責任就是通知它的各觀察者。這給了你在任何時刻增加和刪除觀察者的自由。處理還是忽略一個通知取決于觀察者。

    3) 意外的更新因為一個觀察者并不知道其它觀察者的存在, 它可能對改變目標的最終代價一無所知。在目標上一個看似無害的操作可能會引起一系列對觀察者以及依賴于這些觀察者的那些對象的更新。此外, 如果依賴準則的定義或維護不當,常常會引起錯誤的更新, 這種錯誤通常很難捕捉。

    簡單的更新協(xié)議不提供具體細節(jié)說明目標中什么被改變了, 這就使得上述問題更加嚴重。

    如果沒有其他協(xié)議幫助觀察者發(fā)現(xiàn)什么發(fā)生了改變,它們可能會被迫盡力減少改變。

     

     

     

     

    這一節(jié)討論一些與實現(xiàn)依賴機制相關的問題。

    1) 創(chuàng)建目標到其觀察者之間的映射一個目標對象跟蹤它應通知的觀察者的最簡單的方法是顯式地在目標中保存對它們的引用。然而, 當目標很多而觀察者較少時, 這樣存儲可能代價太高。一個解決辦法是用時間換空間, 用一個關聯(lián)查找機制(例如一個h a s h)來維護目標到觀察者的映射。這樣一個沒有觀察者的目標就不產(chǎn)生存儲開銷。但另一方面, 這一方法增加了訪問觀察者的開銷。

    2) 觀察多個目標在某些情況下, 一個觀察者依賴于多個目標可能是有意義的。例如, 一個表格對象可能依賴于多個數(shù)據(jù)源。在這種情況下, 必須擴展U p d a t e接口以使觀察者知道是哪一個目標送來的通知。目標對象可以簡單地將自己作為U p d a t e操作的一個參數(shù), 讓觀察者知道應去檢查哪一個目標。

    3) 誰觸發(fā)更新目標和它的觀察者依賴于通知機制來保持一致。但到底哪一個對象調(diào)用N o t i f y來觸發(fā)更新? 此時有兩個選擇:

    a) 由目標對象的狀態(tài)設定操作在改變目標對象的狀態(tài)后自動調(diào)用N o t i f y。這種方法的優(yōu)點是客戶不需要記住要在目標對象上調(diào)用N o t i f y,缺點是多個連續(xù)的操作會產(chǎn)生多次連續(xù)的更新, 可能效率較低。

    b) 讓客戶負責在適當?shù)臅r候調(diào)用N o t i f y。這樣做的優(yōu)點是客戶可以在一系列的狀態(tài)改變完成后再一次性地觸發(fā)更新,避免了不必要的中間更新。缺點是給客戶增加了觸發(fā)更新的責任。由于客戶可能會忘記調(diào)用N o t i f y,這種方式較易出錯。

    4) 對已刪除目標的懸掛引用刪除一個目標時應注意不要在其觀察者中遺留對該目標的懸掛引用。一種避免懸掛引用的方法是, 當一個目標被刪除時,讓它通知它的觀察者將對該目標的引用復位。一般來說, 不能簡單地刪除觀察者, 因為其他的對象可能會引用它們, 或者也可能它們還在觀察其他的目標。

    5) 在發(fā)出通知前確保目標的狀態(tài)自身是一致的在發(fā)出通知前確保狀態(tài)自身一致這一點很重要, 因為觀察者在更新其狀態(tài)的過程中需要查詢目標的當前狀態(tài)。當S u b j e c t的子類調(diào)用繼承的該項操作時, 很容易無意中違反這條自身一致的準則。你可以用抽象的S u b j e c t類中的模板方法( Template Method(5.10))發(fā)送通知來避免這種錯誤。定義那些子類可以重定義的原語操作, 并將N o t i f y作為模板方法中的最后一個操作, 這樣當子類重定義了S u b j e c t的操作時,還可以保證該對象的狀態(tài)是自身一致的。

    6) 避免特定于觀察者的更新協(xié)議-/拉模型觀察者模式的實現(xiàn)經(jīng)常需要讓目標廣播關于其改變的其他一些信息。目標將這些信息作為U p d a t e操作一個參數(shù)傳遞出去。這些信息的量可能很小,也可能很大。一個極端情況是,目標向觀察者發(fā)送關于改變的詳細信息, 而不管它們需要與否。我們稱之為推模型(push model)。另一個極端是拉模型(pull model); 目標除最小通知外什么也不送出,而在此之后由觀察者顯式地向目標詢問細節(jié)。

    拉模型強調(diào)的是目標不知道它的觀察者, 而推模型假定目標知道一些觀察者的需要的信息。

    推模型可能使得觀察者相對難以復用,因為目標對觀察者的假定可能并不總是正確的。

    另一方面。拉模型可能效率較差, 因為觀察者對象需在沒有目標對象幫助的情況下確定什么改變了。

    7) 顯式地指定感興趣的改變你可以擴展目標的注冊接口,讓各觀察者注冊為僅對特定事件感興趣,以提高更新的效率。當一個事件發(fā)生時, 目標僅通知那些已注冊為對該事件感興趣的觀察者。支持這種做法一種途徑是,對使用目標對象的方面(a s p e c t s)的概念。可用如下代碼將觀察者對象注冊為對目標對象的某特定事件感興趣:

    void Subject::Attach(Observer*, Aspect& interest);

    此處i n t e r e s t指定感興趣的事件。在通知的時刻, 目標將這方面的改變作為U p d a t e操作的一個參數(shù)提供給它的觀察者,例如:

    void Observer::Update(Subject*, Aspect& interest);

    8) 封裝復雜的更新語義當目標和觀察者間的依賴關系特別復雜時, 可能需要一個維護這些關系的對象。我們稱這樣的對象為更改管理器(C h a n g e M a n a g e r)。它的目的是盡量減少觀察者反映其目標的狀態(tài)變化所需的工作量。例如, 如果一個操作涉及到對幾個相互依賴的目標進行改動, 就必須保證僅在所有的目標都已更改完畢后,才一次性地通知它們的觀察者,而不是每個目標都通知觀察者。

    C h a n g e M a n a g e r有三個責任:

    a) 它將一個目標映射到它的觀察者并提供一個接口來維護這個映射。這就不需要由目標來維護對其觀察者的引用, 反之亦然。

    b) 它定義一個特定的更新策略。

    c) 根據(jù)一個目標的請求, 它更新所有依賴于這個目標的觀察者。

    有兩種特殊的C h a n g e M a n a g e r。S i m p l e C h a n g e M a n a g e r總是更新每一個目標的所有觀察者, 比較簡單。相反,D A G C h a n g e M a n a g e r處理目標及其觀察者之間依賴關系構(gòu)成的無環(huán)有向圖。當一個觀察者觀察多個目標時, DAGChangeManager要比S i m p l e C h a n g e M a n a g e r更好一些。在這種情況下, 兩個或更多個目標中產(chǎn)生的改變可能會產(chǎn)生冗余的更新。D A G C h a n g e M a n a g e r保證觀察者僅接收一個更新。當然,當不存在多重更新的問題時, SimpleChangeManager更好一些。

    C h a n g e M a n a g e r是一個M e d i a t o r ( 5 . 5 )模式的實例。通常只有一個C h a n g e M a n a g e r, 并且它是全局可見的。這里S i n g l e t o n ( 3 . 5 )模式可能有用。

    9) 結(jié)合目標類和觀察者類用不支持多重繼承的語言(S m a l l t a l k )書寫的類庫通常不單獨定義S u b j e c tO b s e r v e r, 而是將它們的接口結(jié)合到一個類中。這就允許你定義一個既是一個目標又是一個觀察者的對象,而不需要多重繼承。例如在S m a l l t a l k, SubjectO b s e r v e r接口

     

    posted on 2007-06-28 18:16 哼哼 閱讀(364) 評論(0)  編輯  收藏 所屬分類: JAVA-Common
    主站蜘蛛池模板: 亚洲男人天堂2017| 麻豆亚洲AV成人无码久久精品 | 久操免费在线观看| 亚洲国色天香视频| 免费a在线观看播放| 久草免费手机视频| 亚洲日本VA午夜在线电影| 亚洲国产美女精品久久久久∴| 114一级毛片免费| 一级一黄在线观看视频免费| 亚洲最大在线观看| 亚洲精品麻豆av| 免费不卡视频一卡二卡| 一级做a爱过程免费视| 亚洲区视频在线观看| 亚洲午夜日韩高清一区| 毛片免费视频观看| 曰批全过程免费视频在线观看无码 | 亚洲乱码国产一区网址| 真人做A免费观看| 中国性猛交xxxxx免费看| 亚洲中文无码永久免| 亚洲嫩草影院久久精品| 国产jizzjizz免费看jizz| 黄色永久免费网站| 国产午夜精品理论片免费观看 | 一个人免费观看视频在线中文| 亚洲国产日产无码精品| 国产综合亚洲专区在线| 在线免费观看一级片| 18女人毛片水真多免费| WWW免费视频在线观看播放| 日本亚洲中午字幕乱码| 亚洲综合欧美色五月俺也去| 亚洲成AV人片在WWW色猫咪| 亚洲人成色7777在线观看不卡| 成人性生交大片免费看午夜a| 99久久免费中文字幕精品| 97在线免费视频| 成年免费a级毛片| 在线亚洲v日韩v|