<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)具備自省的功能,當(dāng)他發(fā)現(xiàn)自己的狀態(tài)改變了,就向相關(guān)的對象發(fā)送消息,這樣的監(jiān)聽方式當(dāng)然比輪詢好。我感冒了自己會去醫(yī)院,用不著醫(yī)生每個月來問一次。

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

    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)聽,當(dāng)然,我們只實現(xiàn)了對add方法的監(jiān)聽,你也可以試試其他的。

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

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

    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)存中進行,而且不需要輪詢,效率非常高,缺點是一旦當(dāng)機內(nèi)存中的數(shù)據(jù)就丟失了,所以如果有一套比較完善的對象緩沖機制,就可以應(yīng)付復(fù)雜的應(yīng)用,寫出高效簡潔的多線程服務(wù)器。

     

     

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

     

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

           先來比較兩張UML圖:

          

    推模式

    拉模式

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

           推模式的優(yōu)點

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

           推模式的缺點:

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

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

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

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

           QCClockDevice不是還要修改嗎?

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

           當(dāng)然拉模式的缺點也是存在的,那就是:

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

     

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

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

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

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

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

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

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

     

     

     

     

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    C h a n g e M a n a g e r有三個責(zé)任:

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

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

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

    有兩種特殊的C h a n g e M a n a g e rS i m p l e C h a n g e M a n a g e r總是更新每一個目標(biāo)的所有觀察者, 比較簡單。相反,D A G C h a n g e M a n a g e r處理目標(biāo)及其觀察者之間依賴關(guān)系構(gòu)成的無環(huán)有向圖。當(dāng)一個觀察者觀察多個目標(biāo)時, DAGChangeManager要比S i m p l e C h a n g e M a n a g e r更好一些。在這種情況下, 兩個或更多個目標(biāo)中產(chǎn)生的改變可能會產(chǎn)生冗余的更新。D A G C h a n g e M a n a g e r保證觀察者僅接收一個更新。當(dāng)然,當(dāng)不存在多重更新的問題時, 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é)合目標(biāo)類和觀察者類用不支持多重繼承的語言(S m a l l t a l k )書寫的類庫通常不單獨定義S u b j e c tO b s e r v e r, 而是將它們的接口結(jié)合到一個類中。這就允許你定義一個既是一個目標(biāo)又是一個觀察者的對象,而不需要多重繼承。例如在S m a l l t a l k, SubjectO b s e r v e r接口

     

    posted on 2007-06-28 18:16 哼哼 閱讀(363) 評論(0)  編輯  收藏 所屬分類: JAVA-Common
    主站蜘蛛池模板: 日韩亚洲人成在线综合| 中文字幕在线成人免费看| 亚洲精品456播放| 任你躁在线精品免费| 67194在线午夜亚洲| 日韩亚洲精品福利| 亚洲黄色免费网址| 日韩在线视频免费| 亚洲精品二三区伊人久久| 中文字幕亚洲日本岛国片| 啦啦啦高清视频在线观看免费| 国产精品免费在线播放| 中文日韩亚洲欧美制服| 亚洲V无码一区二区三区四区观看| 成人男女网18免费视频| 好久久免费视频高清| 特级毛片全部免费播放| 亚洲伊人久久大香线蕉影院| 国产亚洲精品AA片在线观看不加载 | 成人免费无码大片A毛片抽搐| 久久久免费观成人影院| 亚洲最大天堂无码精品区| 久久亚洲国产中v天仙www| 国产做床爱无遮挡免费视频| 又大又硬又爽又粗又快的视频免费| 成人a毛片免费视频观看| 亚洲AV日韩综合一区尤物| 18亚洲男同志videos网站| 亚洲香蕉网久久综合影视| 国产区卡一卡二卡三乱码免费| 91免费国产自产地址入| 久久久免费的精品| 一级免费黄色毛片| 色www免费视频| 亚洲国产精品无码久久98| 2020天堂在线亚洲精品专区| 亚洲日本一区二区三区| 亚洲精品无码AV人在线播放 | 亚洲国产精品成人精品软件| 久久亚洲AV无码精品色午夜麻| 国产日韩成人亚洲丁香婷婷|