21.觀察者模式Observer

1.初識觀察者模式

定義對象間的一種一對多的依賴關(guān)系纵寝,當(dāng)一個對象的狀態(tài)發(fā)生改變時论寨,所有依賴于它的對象都得到通知并被自動更新。

  • Subject:目標(biāo)對象店雅,通常具有如下功能:
    (1)一個目標(biāo)可以被多個觀察者觀察
    (2)目標(biāo)提供對觀察者注冊和退訂的維護(hù)
    (3)當(dāng)目標(biāo)的狀態(tài)發(fā)生變化時政基,目標(biāo)負(fù)責(zé)通知所有注冊的、有效的觀察者
    Observer:定義觀察者的接口闹啦,提供目標(biāo)通知時對應(yīng)的更新方法沮明,這個更新方法進(jìn)行相應(yīng)的業(yè)務(wù)處理,可以在這個方法里面回調(diào)目標(biāo)對象窍奋,以獲取目標(biāo)對象的數(shù)據(jù)荐健。
    ConcreteSubject:具體的目標(biāo)實(shí)現(xiàn)對象酱畅,用來維護(hù)目標(biāo)狀態(tài),當(dāng)目標(biāo)對象的狀態(tài)發(fā)生改變時江场,通知所有注冊有效的觀察者纺酸,讓觀察者執(zhí)行相應(yīng)的處理。
    ConcreteObserver:觀察者的具體實(shí)現(xiàn)對象址否,用來接收目標(biāo)的通知餐蔬,并進(jìn)行相應(yīng)的后續(xù)處理,比如更新自身的狀態(tài)以保持和目標(biāo)的相應(yīng)狀態(tài)一致佑附。

2.體會觀察者模式

2.1 場景問題——訂閱報紙

訂閱報紙的過程


在整個過程中樊诺,郵局只不過起到一個中轉(zhuǎn)的作用,為了簡單音同,我們?nèi)サ羿]局词爬,讓訂閱者直接和報社交互


訂閱報紙的問題:
在上述過程中,訂閱者在完成訂閱后权均,最關(guān)心的問題就是何時能收到新出的報紙顿膨。幸好在現(xiàn)實(shí)生活中,報紙都是定期出版叽赊,這樣發(fā)放到訂閱者手中也基本上有一個大致的時間范圍恋沃,差不多到時間了,訂閱者就會看看郵箱蛇尚,查收新的報紙芽唇。

要是報紙出版的時間不固定呢?

那訂閱者就麻煩了取劫,如果訂閱者想要第一時間閱讀到新報紙,恐怕只能天天守著郵箱了研侣,這未免也太痛苦了吧谱邪。

繼續(xù)引申一下,用類來描述上述的過程庶诡,描述如下:
訂閱者類向出版者類訂閱報紙惦银,很明顯不會只有一個訂閱者訂閱報紙,訂閱者類可以有很多末誓;當(dāng)出版者類出版新報紙的時候扯俱,多個訂閱者類如何知道呢?還有訂閱者類如何得到新報紙的內(nèi)容呢喇澡?

把上面的問題對比描述一下:



進(jìn)一步抽象描述這個問題:當(dāng)一個對象的狀態(tài)發(fā)生改變的時候迅栅,如何讓依賴于它的所有對象得到通知,并進(jìn)行相應(yīng)的處理呢晴玖?

2.2 使用模式的解決方案

3.理解觀察者模式

3.1 認(rèn)識觀察者模式

3.1.1 目標(biāo)和觀察者之間的關(guān)系

按照模式的定義读存,目標(biāo)和觀察者之間是典型的一對多的關(guān)系为流。

但是要注意,如果觀察者只有一個让簿,也是可以的敬察,這樣就變相實(shí)現(xiàn)了目標(biāo)和觀察者之間一對一的關(guān)系,這也使得在處理一個對象的狀態(tài)變化會影響到另一個對象的時候尔当,也可以考慮使用觀察者模式莲祸。

同樣的,一個觀察者也可以觀察多個目標(biāo)椭迎,如果觀察者為多個目標(biāo)定義的通知更新方法都是update方法的話锐帜,這會帶來麻煩,因?yàn)樾枰邮斩鄠€目標(biāo)的通知侠碧,如果是一個update的方法抹估,那就需要在方法內(nèi)部區(qū)分,到底這個更新的通知來自于哪一個目標(biāo)弄兜,不同的目標(biāo)有不同的后續(xù)操作药蜻。

一般情況下,觀察者應(yīng)該為不同的觀察者目標(biāo)替饿,定義不同的回調(diào)方法语泽,這樣實(shí)現(xiàn)最簡單,不需要在update方法內(nèi)部進(jìn)行區(qū)分视卢。

3.1.2 單向依賴

在觀察者模式中踱卵,觀察者和目標(biāo)是單向依賴的,只有觀察者依賴于目標(biāo)据过,而目標(biāo)是不會依賴于觀察者的惋砂。

它們之間聯(lián)系的主動權(quán)掌握在目標(biāo)手中,只有目標(biāo)知道什么時候需要通知觀察者绳锅,在整個過程中西饵,觀察者始終是被動的,被動的等待目標(biāo)的通知鳞芙,等待目標(biāo)傳值給它眷柔。

對目標(biāo)而言,所有的觀察者都是一樣的原朝,目標(biāo)會一視同仁的對待驯嘱。當(dāng)然也可以通過在目標(biāo)里面進(jìn)行控制,實(shí)現(xiàn)有區(qū)別對待觀察者喳坠,比如某些狀態(tài)變化鞠评,只需要通知部分觀察者,但那是屬于稍微變形的用法了丙笋,不屬于標(biāo)準(zhǔn)的谢澈、原始的觀察者模式了煌贴。

3.1.3 基本的實(shí)現(xiàn)說明

具體的目標(biāo)實(shí)現(xiàn)對象要能維護(hù)觀察者的注冊信息,最簡單的實(shí)現(xiàn)方案就如同前面的例子那樣锥忿,采用一個集合來保存觀察者的注冊信息牛郑。

具體的目標(biāo)實(shí)現(xiàn)對象需要維護(hù)引起通知的狀態(tài),一般情況下是目標(biāo)自身的狀態(tài)敬鬓,變形使用的情況下淹朋,也可以是別的對象的狀態(tài)。

具體的觀察者實(shí)現(xiàn)對象需要能接收目標(biāo)的通知钉答,能夠接收目標(biāo)傳遞的數(shù)據(jù)础芍,或者是能夠主動去獲取目標(biāo)的數(shù)據(jù),并進(jìn)行后續(xù)處理数尿。

如果是一個觀察者觀察多個目標(biāo)仑性,那么在觀察者的更新方法里面,需要去判斷是來自哪一個目標(biāo)的通知右蹦。一種簡單的解決方案就是擴(kuò)展update方法诊杆,比如在方法里面多傳遞一個參數(shù)進(jìn)行區(qū)分等;還有一種更簡單的方法何陆,那就是干脆定義不同的回調(diào)方法晨汹。

3.1.4 命名建議

(1)觀察者模式又被稱為發(fā)布-訂閱模式
(2)目標(biāo)接口的定義,建議在名稱后面跟Subject
(3)觀察者接口的定義贷盲,建議在名稱后面跟Observer
(4)觀察者接口的更新方法淘这,建議名稱為update,當(dāng)然方法的參數(shù)可以根據(jù)需要定義巩剖,參數(shù)個數(shù)不限铝穷、參數(shù)類型不限

3.1.5 觸發(fā)通知的時機(jī)

一般情況下,是在完成了狀態(tài)維護(hù)后觸發(fā)佳魔,因?yàn)橥ㄖ獣鬟f數(shù)據(jù)氧骤,不能夠先通知后改數(shù)據(jù),這很容易出問題吃引,會導(dǎo)致觀察者和目標(biāo)對象的狀態(tài)不一致。

3.1.6 相互觀察

A對象的狀態(tài)變化會引起C對象的聯(lián)動操作刽锤,反過來镊尺,C 對象的狀態(tài)變化也會引起A對象的聯(lián)動操作。對于出現(xiàn)這種狀況并思,要特別小心處理庐氮,因?yàn)榭赡軙霈F(xiàn)死循環(huán)的情況。

3.1.7 觀察者模式的調(diào)用順序示意圖

在使用觀察者模式時宋彼,會很明顯的分成兩個階段弄砍,第一個階段是準(zhǔn)備階段仙畦,也就是維護(hù)目標(biāo)和觀察者關(guān)系的階段,這個階段的調(diào)用順序如圖


接下來就是實(shí)際的運(yùn)行階段了音婶,這個階段的調(diào)用順序如圖


3.1.8 通知的順序

從理論上說慨畸,當(dāng)目標(biāo)對象的狀態(tài)變化后通知所有觀察者的時候,順序是不確定的衣式,因此觀察者實(shí)現(xiàn)的功能寸士,絕對不要依賴于通知的順序,也就是說碴卧,多個觀察者之間的功能是平行的弱卡,相互不應(yīng)該有先后的依賴關(guān)系。

3.2 推模型和拉模型

推模型:目標(biāo)對象主動向觀察者推送目標(biāo)的詳細(xì)信息住册,不管觀察者是否需要婶博,推送的信息通常是目標(biāo)對象的全部或部分?jǐn)?shù)據(jù),相當(dāng)于是在廣播通信荧飞。

拉模型:目標(biāo)對象在通知觀察者的時候凡人,只傳遞少量信息,如果觀察者需要更具體的信息垢箕,由觀察者主動到目標(biāo)對象中獲取划栓,相當(dāng)于是觀察者從目標(biāo)對象中拉數(shù)據(jù)。

一般這種模型的實(shí)現(xiàn)中条获,會把目標(biāo)對象自身通過update方法傳遞給觀察者忠荞,這樣在觀察者需要獲取數(shù)據(jù)的時候,就可以通過這個引用來獲取了

關(guān)于兩種模型的比較:
兩種實(shí)現(xiàn)模型帅掘,在開發(fā)的時候委煤,究竟應(yīng)該使用哪一種,還是應(yīng)該具體問題具體分析修档。這里碧绞,只是把兩種模型進(jìn)行一個簡單的比較。

  • 1)推模型是假定目標(biāo)對象知道觀察者需要的數(shù)據(jù)吱窝;而拉模型是目標(biāo)對象不知道觀察者具體需要什么數(shù)據(jù)讥邻,沒有辦法的情況下,干脆把自身傳給觀察者院峡,讓觀察者自己去按需取值兴使。
  • 2)推模型可能會使得觀察者對象難以復(fù)用,因?yàn)橛^察者定義的update方法是按需而定義的照激,可能無法兼顧沒有考慮到的使用情況发魄。這就意味著出現(xiàn)新情況的時候,就可能需要提供新的update方法,或者是干脆重新實(shí)現(xiàn)觀察者励幼。

而拉模型就不會造成這樣的情況汰寓,因?yàn)槔P拖拢瑄pdate方法的參數(shù)是目標(biāo)對象本身苹粟,這基本上是目標(biāo)對象能傳遞的最大數(shù)據(jù)集合了有滑,基本上可以適應(yīng)各種情況的需要。

3.3 Java中的觀察者模式

在java.util包里面有一個類Observable六水,它實(shí)現(xiàn)了大部分我們需要的目標(biāo)的功能俺孙;還有一個接口Observer,它里面定義了update的方法掷贾,就是觀察者的接口睛榄。

3.4 Swing中的觀察者模式

Swing中到處都是觀察者模式的身影,比如大家熟悉的事件處理想帅,就是典型的觀察者模式的應(yīng)用场靴。(說明一下:早期的Swing事件處理用的是職責(zé)鏈)

Swing組件是被觀察的目標(biāo),而每個實(shí)現(xiàn)監(jiān)聽器的類就是觀察者港准,監(jiān)聽器的
接口就是觀察者的接口旨剥,在調(diào)用addXXXListener方法的時候就相當(dāng)于注冊觀察者。

當(dāng)組件被點(diǎn)擊浅缸,狀態(tài)發(fā)生改變的時候轨帜,就會產(chǎn)生相應(yīng)的通知,會調(diào)用注冊的觀察者的方法衩椒,就是我們所實(shí)現(xiàn)的監(jiān)聽器的方法蚌父。

從這里還可以學(xué)一招:如何處理一個觀察者觀察多個目標(biāo)對象

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

  • 觀察者模式實(shí)現(xiàn)了觀察者和目標(biāo)之間的抽象耦合
  • 觀察者模式實(shí)現(xiàn)了動態(tài)聯(lián)動
  • 觀察者模式支持廣播通信
  • 觀察者模式可能會引起無謂的操作

4.思考觀察者模式

4.1 觀察者模式的本質(zhì)

觸發(fā)聯(lián)動

4.2 何時選用

  • 1)當(dāng)一個抽象模型有兩個方面,其中一個方面的操作依賴于另一個方面的狀態(tài)變化毛萌,那么就可以選用觀察者模式苟弛。
  • 2)如果在更改一個對象的時候,需要同時連帶改變其它的對象阁将,而且不知道究竟應(yīng)該有多少對象需要被連帶改變膏秫,這種情況可以選用觀察者模式,被更改的那一個對象很明顯就相當(dāng)于是目標(biāo)對象做盅,而需要連帶修改的多個其它對象缤削,就作為多個觀察者對象了。
  • 3)當(dāng)一個對象必須通知其它的對象吹榴,但是你又希望這個對象和其它被它通知的對象是松散耦合的僻他,也就是說這個對象其實(shí)不想知道具體被通知的對象,這種情況可以選用觀察者模式腊尚,這個對象就相當(dāng)于是目標(biāo)對象,而被它通知的對象就是觀察者對象了满哪。

4.3 簡單變形使用示例(區(qū)別對待觀察者)

1.范例需求
這是一個實(shí)際系統(tǒng)的簡化需求:在一個水質(zhì)監(jiān)測系統(tǒng)中有這樣一個功能婿斥,當(dāng)水中的雜質(zhì)為正常的時候劝篷,只是通知監(jiān)測人員做記錄;當(dāng)為輕度污染的時候民宿,除了通知監(jiān)測人員做記錄外娇妓,還要通知預(yù)警人員,判斷是否需要預(yù)警活鹰;當(dāng)為中度或者高度污染的時候哈恰,除了通知監(jiān)測人員做記錄外,還要通知預(yù)警人員志群,判斷是否需要預(yù)警着绷,同時還要通知監(jiān)測部門領(lǐng)導(dǎo)做相應(yīng)的處理。

2.解決思路和范例代碼
分析上述需求就會發(fā)現(xiàn)锌云,對于水質(zhì)污染這件事情荠医,有可能會涉及到監(jiān)測員、預(yù)警人員桑涎、監(jiān)測部門領(lǐng)導(dǎo)彬向,根據(jù)不同的水質(zhì)污染情況涉及到不同的人員,也就是說攻冷,監(jiān)測員娃胆、預(yù)警人員、監(jiān)測部門領(lǐng)導(dǎo)他們?nèi)呤瞧叫械牡嚷氊?zé)都是處理水質(zhì)污染里烦,但是處理的范圍不一樣。

因此很容易套用上觀察者模式涉兽,如果把水質(zhì)污染的記錄當(dāng)作被觀察的目標(biāo)的話招驴,那么監(jiān)測員、預(yù)警人員和監(jiān)測部門領(lǐng)導(dǎo)就都是觀察者了枷畏。

前面學(xué)過的觀察者模式别厘,當(dāng)目標(biāo)通知觀察者的時候是全部都通知,但是現(xiàn)在這個需求是不同的情況來讓不同的人處理拥诡,怎么辦呢触趴?

解決的方式通常有兩種:
一種是目標(biāo)可以通知,但是觀察者不做任何操作渴肉;
另外一種是在目標(biāo)里面進(jìn)行判斷冗懦,干脆就不通知了。
兩種實(shí)現(xiàn)方式各有千秋仇祭,這里選擇后面一種方式來示例披蕉,這種方式能夠統(tǒng)一邏輯控制,并進(jìn)行觀察者的統(tǒng)一分派,有利于業(yè)務(wù)控制和今后的擴(kuò)展没讲。

5.案例:View-Model

大名鼎鼎的 MVC 模式大家一定都聽過眯娱,MVC 分別指 Model、View爬凑、Controller徙缴。在標(biāo)準(zhǔn) MVC 模型中,當(dāng) Model 改變時嘁信,View 視圖會自動改變于样;View 和 Model 之間就是典型的觀察者模式。

誰是觀察者潘靖,誰是被觀察者穿剖?收通知的就是觀察者。在 View 和 Model 中秘豹,顯然 View 收通知的那一方携御,那么 View 就是觀察者。

在 Web 應(yīng)用中既绕,由于 View 是在瀏覽器端展示啄刹,而 Model 是在服務(wù)端,因此不好體現(xiàn)觀察者模式凄贩,但是在桌面應(yīng)用中體現(xiàn)得淋漓盡致誓军。

另外:MVVM 是雙向綁定,View 改變疲扎,Model 也自動更新昵时。其實(shí)原理很簡單,雙方互為觀察者即可椒丧。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末壹甥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子壶熏,更是在濱河造成了極大的恐慌句柠,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棒假,死亡現(xiàn)場離奇詭異溯职,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)帽哑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進(jìn)店門谜酒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人妻枕,你說我怎么就攤上這事僻族≌吵郏” “怎么了?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵鹰贵,是天一觀的道長晴氨。 經(jīng)常有香客問我,道長碉输,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任亭珍,我火速辦了婚禮敷钾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肄梨。我一直安慰自己阻荒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布众羡。 她就那樣靜靜地躺著侨赡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪粱侣。 梳的紋絲不亂的頭發(fā)上羊壹,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天,我揣著相機(jī)與錄音齐婴,去河邊找鬼油猫。 笑死,一個胖子當(dāng)著我的面吹牛柠偶,可吹牛的內(nèi)容都是我干的情妖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼诱担,長吁一口氣:“原來是場噩夢啊……” “哼毡证!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蔫仙,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤料睛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后匀哄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秦效,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年涎嚼,在試婚紗的時候發(fā)現(xiàn)自己被綠了阱州。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡法梯,死狀恐怖苔货,靈堂內(nèi)的尸體忽然破棺而出犀概,到底是詐尸還是另有隱情,我是刑警寧澤夜惭,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布姻灶,位于F島的核電站,受9級特大地震影響诈茧,放射性物質(zhì)發(fā)生泄漏产喉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一敢会、第九天 我趴在偏房一處隱蔽的房頂上張望曾沈。 院中可真熱鬧,春花似錦鸥昏、人聲如沸塞俱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽障涯。三九已至,卻和暖如春膳汪,著一層夾襖步出監(jiān)牢的瞬間唯蝶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工旅敷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留生棍,地道東北人。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓媳谁,卻偏偏與公主長得像涂滴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子晴音,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評論 2 354

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