觀察者模式Observer

u=4133387865,15189042&fm=21&gp=0.jpg
背景
1.概述
一些面向?qū)ο蟮木幊谭绞?,提供了一種構(gòu)建對象間復(fù)雜網(wǎng)絡(luò)互連的能力。當(dāng)對象們連接在一起時,它們就可以相互提供服務(wù)和信息。
通常來說,當(dāng)某個對象的狀態(tài)發(fā)生改變時,你仍然需要對象之間能互相通信。但是出于各種原因,你也許并不愿意因為代碼環(huán)境的改變而對代碼做大的修改。也許,你只想根據(jù)你的具體應(yīng)用環(huán)境而改進通信代碼?;蛘?,你只想簡單的重新構(gòu)造通信代碼來避免類和類之間的相互依賴與相互從屬。
2.問題
當(dāng)一個對象的狀態(tài)發(fā)生改變時,你如何通知其他對象?是否需要一個動態(tài)方案――一個就像允許腳本的執(zhí)行一樣,允許自由連接的方案?
3.解決方案
觀察模式:定義對象間的一種一對多的依賴關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)生改變時, 所有依賴于它的對象都得到通知并被自動更新。
- 觀察模式允許一個對象關(guān)注其他對象的狀態(tài),并且,觀測模式還為被觀測者提供了一種觀測結(jié)構(gòu),或者說是一個主體和一個客體。主體,也就是被觀測者,可以用來聯(lián)系所有的觀測它的觀測者??腕w,也就是觀測者,用來接受主體狀態(tài)的改變 觀測就是一個可被觀測的類(也就是主題)與一個或多個觀測它的類(也就是客體)的協(xié)作。不論什么時候,當(dāng)被觀測對象的狀態(tài)變化時,所有注冊過的觀測者都會得到通知。
- 觀察模式將被觀測者(主體)從觀測者(客體)種分離出來。這樣,每個觀測者都可以根據(jù)主體的變化分別采取各自的操作。(觀測模式和Publish/Subscribe模式一樣,也是一種有效描述對象間相互作用的模式。)觀測模式靈活而且功能強大。對于被觀測者來說,那些查詢哪些類需要自己的狀態(tài)信息和每次使用那些狀態(tài)信息的額外資源開銷已經(jīng)不存在了。另外,一個觀測者可以在任何合適的時候進行注冊和取消注冊。你也可以定義多個具體的觀測類,以便在實際應(yīng)用中執(zhí)行不同的操作。
- 將一個系統(tǒng)分割成一系列相互協(xié)作的類有一個常見的副作用:需要維護相關(guān)對象間的一致性。我們不希望為了維持一致性而使各類緊密耦合,因為這樣降低了它們的可重用性。
剖析
1.意圖
- 在對象間定義一對多的依賴,以至于當(dāng)一個對象狀態(tài)改變時,依賴者自動收到通知且更新。
- 在Subject中封裝核心組件,在觀察者層次中封裝變量組件
- mvc 中view部分
2.定義
- 定義對象間的一種一對多的依賴關(guān)系。當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴于他的對象都得到通知并被自動更新。
3.本質(zhì)
- 觸發(fā)聯(lián)動
UML

observeUML.png
UML解讀
- 被觀察者:從類圖中可以看到,類中有一個用來存放觀察者對象的Vector容器(之所以使用Vector而不使用List,是因為多線程操作時,Vector在是安全的,而List則是不安全的),這個Vector容器是被觀察者類的核心,另外還有三個方法:attach方法是向這個容器中添加觀察者對象;detach方法是從容器中移除觀察者對象;notify方法是依次調(diào)用觀察者對象的對應(yīng)方法。這個角色可以是接口,也可以是抽象類或者具體的類,因為很多情況下會與其他的模式混用,所以使用抽象類的情況比較多。
- 觀察者:觀察者角色一般是一個接口,它只有一個update方法,在被觀察者狀態(tài)發(fā)生變化時,這個方法就會被觸發(fā)調(diào)用。
- 具體的被觀察者:使用這個角色是為了便于擴展,可以在此角色中定義具體的業(yè)務(wù)邏輯。
- 具體的觀察者:觀察者接口的具體實現(xiàn),在這個角色中,將定義被觀察者對象狀態(tài)發(fā)生變化時所要處理的邏輯。
例子

obserbeExample.png
場景
- ? 當(dāng)一個抽象模型有兩個方面, 其中一個方面依賴于另一方面。將這二者封裝在獨立的對象中以使它們可以各自獨立地改變和復(fù)用。
- ? 當(dāng)對一個對象的改變需要同時改變其它對象 , 而不知道具體有多少對象有待改變。
- ? 當(dāng)一個對象必須通知其它對象,而它又不能假定其它對象是誰。換言之 , 你不希望這些對象是緊密耦合
優(yōu)缺點
優(yōu)點
- 1 )觀察者模式可以實現(xiàn)表示層和數(shù)據(jù)邏輯層的分離,并定義了穩(wěn)定的消息更新傳遞機制,抽象了更新接口,使得可以有各種各樣不同的表示層作為具體觀察者角色。
- 2 )在觀察目標(biāo)和觀察者之間建立一個抽象的耦合 :一個目標(biāo)所知道的僅僅是它有一系列觀察者 , 每個都符合抽象的Observer類的簡單接口。目標(biāo)不知道任何一個觀察者屬于哪一個具體的類。這樣目標(biāo)和觀察者之間的耦合是抽象的和最小的。因為目標(biāo)和觀察者不是緊密耦合的, 它們可以屬于一個系統(tǒng)中的不同抽象層次。一個處于較低層次的目標(biāo)對象可與一個處于較高層次的觀察者通信并通知它 , 這樣就保持了系統(tǒng)層次的完整。如果目標(biāo)和觀察者混在一塊 , 那么得到的對象要么橫貫兩個層次 (違反了層次性), 要么必須放在這兩層的某一層中(這可能會損害層次抽象)。
- 支持廣播通信 :不像通常的請求, 目標(biāo)發(fā)送的通知不需指定它的接收者。通知被自動廣播給所有已向該目標(biāo)對象登記的有關(guān)對象。目標(biāo)對象并不關(guān)心到底有多少對象對自己感興趣 ;它唯一的責(zé)任就是通知它的各觀察者。這給了你在任何時刻增加和刪除觀察者的自由。處理還是忽略一個通知取決于觀察者。
- 觀察者模式符合“開閉原則”的要求。
- 觀察者和被觀察者之間是抽象耦合
- 建立一套觸發(fā)機制
缺點
- 如果一個觀察目標(biāo)對象有很多直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
- 如果在觀察者和觀察目標(biāo)之間有循環(huán)依賴的話,觀察目標(biāo)會觸發(fā)它們之間進行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰。
- 觀察者模式?jīng)]有相應(yīng)的機制讓觀察者知道所觀察的目標(biāo)對象是怎么發(fā)生變化的,而僅僅只是知道觀察目標(biāo)發(fā)生了變化。
模式解讀
經(jīng)驗法則
- 中介者模式間接有發(fā)送者和接收者引用。觀察者模式定義了一個解耦的接口,容許在運行時安裝多個接收者
- 中介者模式 和觀察者模式 是competing patterns.觀察者模式通過觀察者和 subObject發(fā)布交流,而中介者模式在對象間封裝了交流。比復(fù)用中介者模式,我們發(fā)現(xiàn)更容易復(fù)用Observers 和 Subjects.
- 另一方面,中介者模式可以動態(tài)的注冊 colleagues 和他們交流。
分類
- 推模型:目標(biāo)對象主動向觀察者推送目標(biāo)的詳細(xì)信息,不管觀察者是否需要,推送的信息通常是目標(biāo)對象的全部或部分?jǐn)?shù)據(jù),相當(dāng)于廣播通信。
- 拉模型:目標(biāo)對象在通知觀察者的時候,之傳遞少量的信息。如果觀察者需要更具體的信息,由觀察者主動到目標(biāo)對象拉數(shù)據(jù)。一般這種模型的實現(xiàn)中,會把目標(biāo)對象自身通過update方法傳遞給觀察者,這樣子觀察者需要獲取數(shù)據(jù)的時候,就通過這個引用獲取
- 對比:推模型是假定目標(biāo)對象知道觀察者需要的數(shù)據(jù)而拉模型是目標(biāo)對象不知道觀察者需要什么數(shù)據(jù);推模型可能會使得觀察者對象難以復(fù)用。
注意點
- 觸發(fā)機制
- 關(guān)聯(lián)行為場景
- 事件多級觸發(fā)場景
- 跨系統(tǒng)的消息交換場景
- 相互觀察容易出現(xiàn)死循環(huán)
- 數(shù)據(jù)先修改在通知
- 在觀察者模式中,觀察者與目標(biāo)是單向依賴的,只有觀察者依賴于目標(biāo),而目標(biāo)是不會依賴于觀察者
擴展
- 委托就是一種引用方法的類型。一旦為委托分配了方法,委托將于該方法具有完全相同的行為。委托方法的使用可以像其他任何方法一樣,具有參數(shù)和返回值。委托可以看做是對函數(shù)的抽象,是函數(shù)的類,委托的實例將代表一個具體的函數(shù)。
- 一個委托可以搭載多個方法,所有方法被一次喚起。 可以使得委托對象所搭載的方法并不需要屬于同一個類。
- 委托對象所搭載的所有方法必須具有相同的原型和形式,也就是擁有相同參數(shù)列表和返回值類型。