淺析設(shè)計(jì)模式之觀察者模式

在面向?qū)ο缶幊讨校^察者模式使用率非常高,最近流行的響應(yīng)式編程框架RxJava就對(duì)觀察者模式做出了擴(kuò)展。了解了觀察者模式也有助于對(duì)Rxjava的學(xué)習(xí)。

定義

觀察者模式中有兩個(gè)角色,被觀察者與觀察者,它們之間是一種一對(duì)多的依賴關(guān)系,當(dāng)被觀察者的狀態(tài)發(fā)生改變時(shí),觀察者也發(fā)生相應(yīng)的行為。在現(xiàn)實(shí)生活中很容易找出這樣的例子,比如教室里的教師和學(xué)生聽到下課鈴聲響了之后下課休息,教師和學(xué)生是觀察者,下課鈴是被觀察者,下課鈴響了也就是被觀察者的狀態(tài)發(fā)生改變,教師和學(xué)生這些觀察者做出相應(yīng)的行動(dòng)。另外,觀察者模式也可以稱為訂閱-發(fā)布模式,因?yàn)橛^察者與被觀察者的一系列活動(dòng)可以簡單概括為訂閱和發(fā)布。使用類圖表示觀察者模式,如下圖

subject可以是一個(gè)抽象類或普通類,concreteSubject是其子類,是具體的被觀察者,observer是一個(gè)只包含update(Object)方法的接口,實(shí)現(xiàn)該接口的ConcreteObserver為具體的觀察者。在jdk中就有內(nèi)置的類型Observable,Observer方便開發(fā)者使用觀察者模式。被觀察者需要繼承Observable類,而被觀察者則需要實(shí)現(xiàn)Observer接口。

再舉個(gè)例子,學(xué)生聽到廣播后做出行為。代碼如下:

觀察者Student

//Student.java

public class Student implements Observer {

    private String name;

    public Student(String name){
        this.name = name;
    }

    @Override
    public void update(Observable o, Object arg) {
        System.out.println(name +","+ arg);
    }
}

被觀察者Broadcast

// Broadcast.java

public class Broadcast extends Observable {

    public void  sendBroadcast(String content){

        // 標(biāo)識(shí)狀態(tài)或內(nèi)容發(fā)生改變
        setChanged();

        // 通知所有觀察者
        notifyObservers(content);
    }

}

測試類

// Test.java

public class Test {
    public static void main(String[] args) {

        Broadcast broadcast = new Broadcast();

        Student student1 = new Student("黃同學(xué)");
        Student student2 = new Student("王同學(xué)");
        Student student3 = new Student("楊同學(xué)");

        brocast.addObserver(student1);
        brocast.addObserver(student2);
        brocast.addObserver(student3);

        brocast.sendBroadcast("聽到廣播后請(qǐng)到辦公室一趟!");
    }
}

結(jié)果輸出

楊同學(xué),聽到廣播后請(qǐng)到辦公室一趟!
王同學(xué),聽到廣播后請(qǐng)到辦公室一趟!
黃同學(xué),聽到廣播后請(qǐng)到辦公室一趟!

觀察者無需時(shí)刻觀察著被觀察者,就如以上兩個(gè)例子,學(xué)生無需時(shí)刻關(guān)注下課鈴聲以及廣播,只有當(dāng)被觀察者狀態(tài)改變,觀察者才能有下一步的行動(dòng)。

Observer和Observable的實(shí)現(xiàn)也很簡單,以下就是源碼:

Observer只是一個(gè)接口

// Observer.java

public interface Observer {
    void update(Observable o, Object arg);
}

Observable需要確保相關(guān)操作是線程安全的,同時(shí)使用一個(gè)Vector來存放觀察者對(duì)象。

// Observable.java

public class Observable {

    private boolean changed = false;

    private Vector obs;

    public Observable() {
        obs = new Vector();
    }

    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }
    
    public void notifyObservers() {
        notifyObservers(null);
    }

    public void notifyObservers(Object arg) {

        Object[] arrLocal;

        synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }
 
    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

    protected synchronized void setChanged() {
        changed = true;
    }

    protected synchronized void clearChanged() {
        changed = false;
    }

    public synchronized boolean hasChanged() {
        return changed;
    }

    public synchronized int countObservers() {
        return obs.size();
    }
}

可以看到,Observable內(nèi)部使用一個(gè)Vector來存儲(chǔ)觀察者對(duì)象,并且提供增加,刪除以及通知觀察者角色的方法,還有一點(diǎn)需要注意的就是,notifyObservers(Object arg)方法內(nèi)部有一個(gè)判斷標(biāo)識(shí),因此具體的被觀察者角色需要先調(diào)用setChanged()以標(biāo)識(shí)狀態(tài)發(fā)生改變,notifyObservers(Object arg)方法才能有效。

當(dāng)然,我們完全可以自己實(shí)現(xiàn)抽象的主題角色和抽象的觀察者角色,只是jdk已經(jīng)幫我們實(shí)現(xiàn)了,無需我們重復(fù)造輪子而已。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,577評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,600評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,944評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評(píng)論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,108評(píng)論 0 290
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,652評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,385評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,616評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評(píng)論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,798評(píng)論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評(píng)論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,334評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,570評(píng)論 2 379

推薦閱讀更多精彩內(nèi)容