觀察者模式

什么是觀察者模式##

有人這么說

觀察者模式定義了一種一對(duì)多的依賴關(guān)系,讓多個(gè)觀察者對(duì)象同時(shí)監(jiān)聽某一個(gè)主題對(duì)象。

這個(gè)主題對(duì)象在狀態(tài)上發(fā)生變化時(shí),會(huì)通知所有觀察者對(duì)象,讓它們能夠自動(dòng)更新自己。

還有人這么說

觀察者模式是關(guān)于多個(gè)對(duì)象想知道一個(gè)對(duì)象中數(shù)據(jù)變化情況的一種成熟模式。觀察者模式中有一個(gè)稱作“主題”的對(duì)象和若干個(gè)稱作“觀察者”的對(duì)象,“主題”和“觀察者”之間是一種一對(duì)多的依賴關(guān)系。

當(dāng)“主題”的狀態(tài)發(fā)生變化時(shí),所有“觀察者”都得到通知。

日常生活中,最容易理解的例子就是微信公眾號(hào)。我們用微信訂閱的微信公共號(hào)就是這里所說的主題,而我們 每一個(gè)關(guān)注這個(gè)微信號(hào)的人就是這里的觀察者。公眾號(hào)每天有更新,所有訂閱者都會(huì)收到。

這里寫圖片描述

觀察者模式類圖

應(yīng)用場景##

此種模式通常被用來實(shí)現(xiàn)事件處理系統(tǒng)。

觀察者模式組成##

從定義可以看到,該模式必須包含兩個(gè)角色:觀察者和被觀察對(duì)象(主題)。

從代碼實(shí)現(xiàn)的角度,我們又可以分為以下四種角色:

  • 抽象主題角色: 把所有對(duì)觀察者對(duì)象的引用保存在一個(gè)集合中,每個(gè)抽象主題角色都可以有任意數(shù)量的觀察者。抽象主題提供一個(gè)接口,可以增加和刪除觀察者角色。一般用一個(gè)抽象類和接口來實(shí)現(xiàn)。

  • 抽象觀察者角色:為所有具體的觀察者定義一個(gè)接口,在得到主題的通知時(shí)更新自己。

  • 具體主題角色:在具體主題內(nèi)部狀態(tài)改變時(shí),給所有登記過的觀察者發(fā)出通知。具體主題角色通常用一個(gè)子類實(shí)現(xiàn)。

  • 具體觀察者角色:該角色實(shí)現(xiàn)抽象觀察者角色所要求的更新接口,以便使本身的狀態(tài)與主題的狀態(tài)相協(xié)調(diào)。通常用一個(gè)子類實(shí)現(xiàn)。如果需要,具體觀察者角色可以保存一個(gè)指向具體主題角色的引用。

實(shí)現(xiàn)觀察者模式##

我們就按照上面提到的4中角色,依次實(shí)現(xiàn):

  • 抽象主題角色

主題接口規(guī)定了具體主題需要實(shí)現(xiàn)的添加,刪除及通知觀察者更新數(shù)據(jù)的方法

/**
 * 抽象主題,被觀察者
 *
 */
public interface Subject {
    /**
     * 添加觀察者
     * 
     * @param observer
     */
    void addObserver(Observer observer);

    /**
     * 移除指定的觀察者
     * 
     * @param observer
     */
    void removeObserver(Observer observer);

    /**
     * 移除所有的觀察者
     */
    void removeAll();

    /**
     * data 是要通知給觀察者的數(shù)據(jù) 因?yàn)镺bject是所有類的父類,可以使用多態(tài),當(dāng)然 你也可以使用 泛型
     * 
     * @param data
     */
    void notifyAllObserver(Object data);

    /**
     * 單獨(dú) 通知某一個(gè)觀察者
     * 
     * @param observer
     * @param data
     *            data 是要通知給觀察者的數(shù)據(jù) 因?yàn)镺bject是所有類的父類,可以使用多態(tài),當(dāng)然 你也可以使用 泛型
     */
    void notify(Observer observer, Object data);

}
  • 抽象觀察者角色

觀察者接口規(guī)定了具體觀察者用來更新數(shù)據(jù)的方法

/**
 * 抽象觀察者接口
 */
public interface Observer {
    /**
     * 
     * @param subject 被觀察者
     * @param data    被觀察者傳遞給觀察者的 數(shù)據(jù)
     */
    void update(Subject subject,Object data);
}
  • 具體主題角色
public class ConcreteSubject implements Subject {

    //觀察者集合,用于管理所有的觀察者
    List<Observer> mList = new ArrayList<>();

    @Override
    public void addObserver(Observer observer) {
        // TODO Auto-generated method stub
        // 確保相同的觀察者只含有一個(gè)
        if (observer == null) {
            throw new NullPointerException("observer == null");
        }

        if (!mList.contains(observer)) {
            mList.add(observer);
        }
    }

    @Override
    public void removeObserver(Observer observer) {
        // TODO Auto-generated method stub
        mList.remove(observer);
    }

    @Override
    public void removeAll() {
        // TODO Auto-generated method stub
        mList.clear();
    }

    @Override
    public void notifyAllObserver(Object data) {
        // TODO Auto-generated method stub
        for (Observer observer : mList) {
            observer.update(this, data);
        }
    }

    @Override
    public void notify(Observer observer, Object data) {
        // TODO Auto-generated method stub
        if (observer != null) {
            observer.update(this, data);
        }
    }

}
  • 具體的觀察者角色

這里我們可以定義多個(gè)具體的觀察者角色

觀察者One

public class ObserverOne implements Observer {

    @Override
    public void update(Subject subject, Object data) {
        // TODO Auto-generated method stub
        System.err
                .println("the messge from subject to-->" + this.getClass().getName() + "<---is " + data.toString());
    }

}

觀察者Two

public class ObserverTwo implements Observer {

    @Override
    public void update(Subject subject, Object data) {
        // TODO Auto-generated method stub
        System.err
        .println("the messge from subject to-->" + this.getClass().getName() + "<---is " + data.toString());
    }

}

觀察者Three

public class ObserverThree implements Observer {

    @Override
    public void update(Subject subject, Object data) {
        // TODO Auto-generated method stub
        System.err
        .println("the messge from subject to-->" + this.getClass().getName() + "<---is " + data.toString());
    }

}

好了,到了這里我們就完成了所有角色的定義。寫個(gè)方法測試一下:

  • 測試類
public class TestObservePattern {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ConcreteSubject concreteSubject = new ConcreteSubject();
        ObserverOne observerOne=new ObserverOne();
        ObserverTwo observerTwo=new ObserverTwo();
        ObserverThree observerThree=new ObserverThree();
        
        concreteSubject.addObserver(observerOne);
        concreteSubject.addObserver(observerTwo);
        concreteSubject.addObserver(observerThree);
        
        
        //通知所有的觀察者
        concreteSubject.notifyAllObserver("wake up,wake up");
        //通知某個(gè)特定的觀察者OberverTwo
        concreteSubject.notify(observerTwo, "Specila msg  for you");
        //觀察者ObserveThree 決定不再訂閱主題
        concreteSubject.removeObserver(observerThree);
        //通知所有的觀察者
        concreteSubject.notifyAllObserver("new Message come ");
    }

}

運(yùn)行程序后輸入日志如下:

通過日志可以看到:

  • 主題內(nèi)容更新后,所有的觀察者將接收到更新結(jié)果。
  • 某個(gè)特定的觀察者取消對(duì)主題的訂閱后,自身不再接收到主題的更新,而且也不影響主題的實(shí)現(xiàn)。

和設(shè)置監(jiān)聽器機(jī)制的區(qū)別##

初次看到觀察者模式的定義時(shí),感覺這種套路似曾相識(shí)。思前想后才發(fā)現(xiàn),觀察者模式其實(shí)和平時(shí)給Button設(shè)置點(diǎn)擊事件的實(shí)現(xiàn)方式有些類似。都是作為主題(Button)發(fā)生變化(被用戶點(diǎn)擊),觀察者(OnClickListener)接收到通知,并作出響應(yīng)(onClick回調(diào)方法執(zhí)行)。

看到很多地方將觀察者模式和設(shè)置監(jiān)聽器模式(機(jī)制)歸為同一種模式,個(gè)人感覺是不太恰當(dāng)?shù)摹?/p>

  • 首先,觀察者模式中,抽象觀察者角色以接口的形式存在,注定了所有的具體觀察者角色實(shí)現(xiàn)更新(如此處的update)時(shí),方法是唯一的,只有update。 而設(shè)置監(jiān)聽器機(jī)制中,為主題(事件源)設(shè)置不同的觀察者(監(jiān)聽器),主題(事件源)發(fā)生變化后,觀察者所需實(shí)現(xiàn)的方法也是不唯一的。我們這里以Button的click事件和Longclick事件為例:
不同事件

可以看到,不同的觀察者所需實(shí)現(xiàn)的方法是完全不一樣的。

  • 其次,觀察者某事中,被觀察者發(fā)生變化時(shí),所有觀察者將被動(dòng)接受通知 。所有具體的觀察者根本無法區(qū)分到底是發(fā)生了怎樣的變化。當(dāng)然,也可以通過在主題發(fā)送通知時(shí)根據(jù)不同的狀態(tài)分別通知不同的觀察者。但是,這樣就使得被觀察和觀察者之間有了聯(lián)系,這是不好的思路。而監(jiān)聽器機(jī)制,通過設(shè)置不同的監(jiān)聽器(即不同的觀察者),便解決了這個(gè)問題。

所以,監(jiān)聽器機(jī)制相較于嚴(yán)格的觀察者模式還是有區(qū)別的。

觀察者模式優(yōu)缺點(diǎn)##

以下內(nèi)容摘抄自網(wǎng)絡(luò)

觀察者模式有以下的優(yōu)點(diǎn):

第一、觀察者模式在被觀察者和觀察者之間建立一個(gè)抽象的耦合。被觀察者角色所知道的只是一個(gè)具體觀察者列表,每一個(gè)具體觀察者都符合一個(gè)抽象觀察者的接口。被觀察者并不認(rèn)識(shí)任何一個(gè)具體觀察者,它只知道它們都有一個(gè)共同的接口。由于被觀察者和觀察者沒有緊密地耦合在一起,因此它們可以屬于不同的抽象化層次。如果被觀察者和觀察者都被扔到一起,那么這個(gè)對(duì)象必然跨越抽象化和具體化層次。

第二、觀察者模式支持廣播通訊。被觀察者會(huì)向所有的登記過的觀察者發(fā)出通知,

觀察者模式有下面的缺點(diǎn):

第一、如果一個(gè)被觀察者對(duì)象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會(huì)花費(fèi)很多時(shí)間。

第二、如果在被觀察者之間有循環(huán)依賴的話,被觀察者會(huì)觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,導(dǎo)致系統(tǒng)崩潰。在使用觀察者模式是要特別注意這一點(diǎn)。

第三、如果對(duì)觀察者的通知是通過另外的線程進(jìn)行異步投遞的話,系統(tǒng)必須保證投遞是以自恰的方式進(jìn)行的。

第四、雖然觀察者模式可以隨時(shí)使觀察者知道所觀察的對(duì)象發(fā)生了變化,但是觀察者模式?jīng)]有相應(yīng)的機(jī)制使觀察者知道所觀察的對(duì)象是怎么發(fā)生變化的。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容