設(shè)計模式之觀察者模式

一、觀察者模式的定義

定義對象間一對多的依賴關(guān)系葫盼,使得當(dāng)前對象改變了狀態(tài)贫导,則所有依賴于它的對象都會得到通知并自動更新蟆盹。

二逾滥、觀察者模式的使用場景

  1. 一個抽象模型有兩個方面,其中一個方面依賴于另一個方面讥巡;(比如:View依賴于Model的數(shù)據(jù)欢顷,當(dāng)數(shù)據(jù)發(fā)生改變時抬驴,更新View)
  2. 一個對象的改變將導(dǎo)致一個或多個其他對象也發(fā)生改變豌拙;(比如:同一個數(shù)據(jù)源對多個觀察對象)
  3. 需要在系統(tǒng)創(chuàng)建一個觸發(fā)鏈姆蘸。(比如A影響B(tài)芙委,B影響C)

三灌侣、UML結(jié)構(gòu)圖

觀察者模式 UML
  • Subject:抽象主題侧啼,也就是被觀察者(Observable)痊乾,把所有的觀察者對象的引用保存在一個集合里。每個主題都可以有任意數(shù)量的觀察者蛾魄,抽象主題提供一個接口滴须,可以增加和刪除觀察者對象叽奥。
  • ConcreteSubject:具體主題朝氓,該角色將有關(guān)狀態(tài)存入具體觀察者對象主届,在具體主題內(nèi)部狀態(tài)發(fā)生改變時岂膳,會給所有注冊過的觀察者發(fā)出通知notify。
  • Observer:抽象觀察者涧偷,它定義了更新接口燎潮,使得在得到被觀察者都更改通知時更新自己确封。
  • ConcreteObserver:具體觀察者爪喘,實現(xiàn)了抽象觀察者所定義的更新接口秉剑。

四侦鹏、實現(xiàn)1(自己寫Observer和Observable)

/*
 * 抽象被觀察者略水,添加刪除通知觀察者渊涝。
 */
public abstract class Subject {
    //保存注冊觀察者對象
    private Vector<Observer> mObservers = new Vector<>();
    //注冊觀察者對象
    public void addObserver(Observer o) {
        this.mObservers.add(o);
    }
    //注銷觀察者對象
    public void removeObserver(Observer o) {
        this.mObservers.remove(o);
    }
    //通知觀察者對象
    public void notifyObservers() {
        for (Observer o : this.mObservers) {
            o.update();
        }
    }
}
/*
 * 具體被觀察者
 */
public class ConcreteSubject extends Subject {
    public void doSomething() {
        System.out.println("doSomething");
        this.notifyObservers();
    }
}
/*
 * 抽象觀察者
 */
public interface Observer {
    void update();
}
/*
 * 具體觀察者
 */
public class ConcreteObserver implements Observer {
    @Override
    public void update() {
        System.out.println("收到消息");
    }
}
/*
 * 測試類
 */
public class Client {
    public static void main(String[] args) {
        ConcreteSubject cs = new ConcreteSubject();
        Observer observer1 = new ConcreteObserver();
        Observer observer2 = new ConcreteObserver();
        cs.addObserver(observer1);
        cs.addObserver(observer2);
        cs.doSomething();
    }
}
  • 顯示結(jié)果:
doSomething
收到消息
收到消息

五、實現(xiàn)2(實際開發(fā)中一般是使用java.util包下面的Observer煤傍、Observable)

/*
 * 觀察者
 */
class EmailUser implements Observer {
    public String name;
    public EmailUser(String name) {
        this.name = name;
    }
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("親" + name + "蚯姆," +
                "你關(guān)注的欄目《" + arg + "》已經(jīng)更新,請及時查看疙驾!");
    }
}
/*
 *被觀察者
 */
class Emailcolumn extends Observable {
    public void postNewColumn(String content) {
        setChanged();
        notifyObservers(content);
    }
}
/*
 * 測試代碼
 */
public class ObserverDemo {
    public static void main(String[] args) {
        Emailcolumn emailcolumn = new Emailcolumn();
        EmailUser user1 = new EmailUser("user1");
        EmailUser user2 = new EmailUser("user2");
        emailcolumn.addObserver(user1);
        emailcolumn.addObserver(user2);
        emailcolumn.postNewColumn("今天最開心:雷佳音");
    }
}
  • 顯示結(jié)果:
親user2它碎,你關(guān)注的欄目《今天最開心:雷佳音》已經(jīng)更新扳肛,請及時查看挖息!
親user1兽肤,你關(guān)注的欄目《今天最開心:雷佳音》已經(jīng)更新资铡,請及時查看害驹!

六、觀察者模式在Android中的實際運用

  1. 回調(diào)函數(shù):一對一的關(guān)系
  2. ListView數(shù)據(jù)更新
  3. RxJava
  4. 廣播注冊
  5. EventBus

1.回調(diào)函數(shù)

定義:實現(xiàn)了抽象類/接口的實例宛官,實現(xiàn)了父類提供的抽象方法后葫松,將該方法交還給父類來處理。

public class Employee {
    //定義回調(diào)接口的成員變量
    private Callback mcallback;
    //聲明回調(diào)接口
    public interface Callback {
        public abstract void work();
    }
    //設(shè)置回調(diào)接口對象成員變量
    public void setCallback(Callback callback) {
        this.mcallback = callback;
    }
    //調(diào)用回調(diào)接口對象中的方法
    public void doWork() {
        if(mcallback!=null){
            mcallback.work();
        }
    }
}
public class Boss {
    private Employee employee;
    //為Employee設(shè)置回調(diào)函數(shù)底洗,在這里定義具體的回調(diào)方法
    public void setCallback() {
        employee.setCallback(new Employee.Callback() {
            @Override
            public void work() {
                System.out.println("work");
            }
        });
    }
}

再來看另一個回調(diào)的例子:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // do something
    }
});

OnClickListener作為觀察者腋么,button作為被觀察者,通過setOnClickListener來形成訂閱關(guān)系亥揖。當(dāng)button被點擊時相當(dāng)于被觀察者數(shù)據(jù)發(fā)生改變珊擂,就會去通知觀察者OnClickListener,這時觸發(fā)onClick(回調(diào)onClick)费变。

  • 面試題:回調(diào)函數(shù)和觀察者模式的區(qū)別董虱?
  • 觀察者模式:定義了一種一對多的依賴關(guān)系键兜,讓多個觀察者對象同時監(jiān)聽某一個主題對象隔躲。觀察者模式完美的將觀察者和被觀察的對象分離開,一個對象的狀態(tài)發(fā)生變化時用含,所有依賴于它的對象都得到通知并自動刷新。
  • 回調(diào)函數(shù):其實也算是一種觀察者模式的實現(xiàn)方式缸夹,回調(diào)函數(shù)實現(xiàn)的觀察者和被觀察者往往是一對一的依賴關(guān)系。
  • 所以最明顯的區(qū)別是觀察者模式是一種設(shè)計思路猫态,而回調(diào)函數(shù)式一種具體的實現(xiàn)方式勇凭;另一明顯區(qū)別是一對多還是多對多的依賴關(guān)系方面灌砖。

相關(guān)閱讀:Android面試一天一題(Day 43:設(shè)計模式)

2.ListView的notifyDataSetChanged

當(dāng) ListView 的數(shù)據(jù)發(fā)生變化時,調(diào)用 Adapter 的 notifyDataSetChanged 函數(shù)库继,這個函數(shù)又會調(diào)用 DataSetObservable 的 notifyChanged 函數(shù),這個函數(shù)會調(diào)用所有觀察者 (AdapterDataSetObserver) 的 onChanged 方法,在 onChanged 函數(shù)中又會調(diào)用 ListView 重新布局的函數(shù) requestLayout()使得 ListView 刷新界面撒妈。

七、觀察者模式的缺點

  • 開發(fā)效率問題棋蚌。一個被觀察者,多個觀察者,開發(fā)和調(diào)試就會比較復(fù)雜。
  • 運行效率問題班利。多個觀察者、多級觸發(fā)等情況下闯割,因為在Java默認(rèn)是順序執(zhí)行,所以一個觀察者卡殼,會影響整體的執(zhí)行效率,這時候推薦考慮采用異步的方式缎谷。

八、觀察者模式的優(yōu)點

  • 觀察者和被觀察者之間是抽象耦合者甲,觀察者和被觀察者的擴展比較方便刽辙。
  • 建立一套觸發(fā)機制。例如建立一條觸發(fā)鏈。

九、總結(jié)

觀察者模式主要的作用就是對象解耦,將觀察者與被觀察者完全隔離际歼,只依賴與 Observer 和 Obserable 抽象旭愧。例如LisetView就是運用了Adapter和觀察者模式使得它的可擴展性桃熄、靈活性非常強碉京,而耦合的很低,這些設(shè)計模式在Android源碼中優(yōu)秀運用的典范咽瓷。

  • 那么如何達(dá)到低耦合月匣、高靈活性的代碼?
  • 廣播接收器和時間總線在觀察者模式上有什么相似和不相同的地方头遭?
  • RxJava是怎么運用觀察者模式的鲫惶?

十艺蝴、參考書籍

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末胯盯,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖朝聋,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猜极,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機华望,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旭绒,“玉大人,你說我怎么就攤上這事∨” “怎么了嚣艇?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵冈爹,是天一觀的道長恳谎。 經(jīng)常有香客問我,道長评肆,這世上最難降的妖魔是什么盹廷? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任畜晰,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘块蚌。我一直安慰自己闰非,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布峭范。 她就那樣靜靜地躺著财松,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纱控。 梳的紋絲不亂的頭發(fā)上辆毡,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天,我揣著相機與錄音甜害,去河邊找鬼舶掖。 笑死,一個胖子當(dāng)著我的面吹牛尔店,可吹牛的內(nèi)容都是我干的眨攘。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼嚣州,長吁一口氣:“原來是場噩夢啊……” “哼鲫售!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起避诽,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤龟虎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后沙庐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鲤妥,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年拱雏,在試婚紗的時候發(fā)現(xiàn)自己被綠了棉安。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡铸抑,死狀恐怖贡耽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鹊汛,我是刑警寧澤蒲赂,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站刁憋,受9級特大地震影響滥嘴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜至耻,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一若皱、第九天 我趴在偏房一處隱蔽的房頂上張望镊叁。 院中可真熱鬧,春花似錦走触、人聲如沸晦譬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽敛腌。三九已至,卻和暖如春惫皱,著一層夾襖步出監(jiān)牢的瞬間迎瞧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工逸吵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留凶硅,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓扫皱,卻偏偏與公主長得像足绅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子韩脑,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

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

  • 用最簡單的一句話來理解觀察者模式就是:當(dāng)一個對象發(fā)生改變時氢妈,其相關(guān)依賴對象皆得到通知并被自動更新。 關(guān)于這個圖的四...
    芒果味的你呀閱讀 1,377評論 0 9
  • 參考 《設(shè)計模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ) 》5.7 Observer 觀察者 對象行為型模式 《設(shè)計模式解析》 ...
    WangGavin閱讀 515評論 0 2
  • 客戶需求 程序設(shè)計 一個氣象站對應(yīng)著多個客戶端段多,氣象站的數(shù)據(jù)一發(fā)生變化首量,客戶端的數(shù)據(jù)也要隨著更新,這就形成了一種依...
    BlainPeng閱讀 996評論 1 17
  • 前言定義:觀察者設(shè)計模式定義了對象間的一種一對多的依賴關(guān)系进苍,以便一個對象的狀態(tài)發(fā)生變化時加缘,所有依賴于它的對象都得到...
    xsp單細(xì)胞閱讀 453評論 0 1
  • 文|程宜家 “想念曾經(jīng)最溫暖的海底,但大海無邊無際觉啊,我還能不...
    程宜家閱讀 500評論 13 9