定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於它的對象都獲得通知並被自動更新。也叫訂閱者模式。java
大數據
spa
server
對象
blog
接口
開發
ConcreteSubject:class
具體的目標實現對象,用來維護目標狀態,當目標對象的狀態發生改變時,通知全部註冊有效的觀察者,讓觀察者執行相應的處理。擴展
ConcreteObserver:
觀察者的具體實現對象,用來接收目標的通知,並進行相應的後續處理,好比更新自身的狀態以保持和目標的相應狀態一致。
訂閱報紙的過程
在整個過程當中,郵局只不過起到一箇中轉的做用,爲了簡單,咱們去掉郵局,讓訂閱者直接和報社交互
繼續引伸一下,用類來描述上述的過程,描述以下:
訂閱者類向出版者類訂閱報紙,很明顯不會只有一個訂閱者訂閱報紙,訂閱者類能夠有不少;當出版者類出版新報紙的時候,多個訂閱者類如何知道呢?還有訂閱者類如何獲得新報紙的內容呢?
把上面的問題對比描述一下:
使用模式的解決方案
1:目標和觀察者之間的關係 按照模式的定義,目標和觀察者之間是典型的一對多的關係。可是要注意,若是觀察者只有一個,也是能夠的,這樣就變相實現了目標和觀察者之間一對一的關係,這也使得在處理一個對象的狀態變化會影響到另外一個對象的時候,也能夠考慮使用觀察者模式。
一樣的,一個觀察者也能夠觀察多個目標,若是觀察者爲多個目標定義的通知更新方法都是update方法的話,這會帶來麻煩,由於須要接收多個目標的通知,若是是一個update的方法,那就須要在方法內部區分,到底這個更新的通知來自於哪個目標,不一樣的目標有不一樣的後續操做。 通常狀況下,觀察者應該爲不一樣的觀察者目標,定義不一樣的回調方法,這樣實現最簡單,不須要在update方法內部進行區分。
2:單向依賴 在觀察者模式中,觀察者和目標是單向依賴的,只有觀察者依賴於目標,而目標是不會依賴於觀察者的。它們之間聯繫的主動權掌握在目標手中,只有目標知道何時須要通知觀察者,在整個過程當中,觀察者始終是被動的,被動的等待目標的通知,等待目標傳值給它。
對目標而言,全部的觀察者都是同樣的,目標會一視同仁的對待。固然也能夠經過在目標裏面進行控制,實現有區別對待觀察者,好比某些狀態變化,只須要通知部分觀察者,但那是屬於稍微變形的用法了,不屬於標準的、原始的觀察者模式了。
3:基本的實現說明 具體的目標實現對象要能維護觀察者的註冊信息,最簡單的實現方案就如同前面的例子那樣,採用一個集合來保存觀察者的註冊信息。具體的目標實現對象須要維護引發通知的狀態,通常狀況下是目標自身的狀態,變形使用的狀況下,也能夠是別的對象的狀態。 具體的觀察者實現對象須要能接收目標的通知,可以接收目標傳遞的數據,或者是可以主動去獲取目標的數據,並進行後續處理。
若是是一個觀察者觀察多個目標,那麼在觀察者的更新方法裏面,須要去判斷是來自哪個目標的通知。一種簡單的解決方案就是擴展update方法,好比在方法裏面多傳遞一個參數進行區分等;還有一種更簡單的方法,那就是乾脆定義不一樣的回調方法。
4:命名建議
(1)觀察者模式又被稱爲發佈-訂閱模式
(2)目標接口的定義,建議在名稱後面跟Subject
(3)觀察者接口的定義,建議在名稱後面跟Observer
(4)觀察者接口的更新方法,建議名稱爲update,固然方法的參數能夠根據須要定義,參數個數不限、參數類型不限
5:觸發通知的時機通常狀況下,是在完成了狀態維護後觸發,由於通知會傳遞數據,不可以先通知後改數據,這很容易出問題,會致使觀察者和目標對象的狀態不一致。
6:相互觀察A對象的狀態變化會引發C對象的聯動操做,反過來,C 對象的狀態變化也會引發A對象的聯動操做。對於出現這種情況,要特別當心處理,由於可能會出現死循環的狀況。
7:觀察者模式的調用順序示意圖在使用觀察者模式時,會很明顯的分紅兩個階段,第一個階段是準備階段,也就是維護目標和觀察者關係的階段,這個階段的調用順序如圖
接下來就是實際的運行階段了,這個階段的調用順序如圖
推模型:
目標對象主動向觀察者推送目標的詳細信息,無論觀察者是否須要,推送的信息一般是目標對象的所有或部分數據,至關因而在廣播通訊。
拉模型:
目標對象在通知觀察者的時候,只傳遞少許信息,若是觀察者須要更具體的信息,由觀察者主動到目標對象中獲取,至關因而觀察者從目標對象中拉數據。通常這種模型的實現中,會把目標對象自身經過update方法傳遞給觀察者,這樣在觀察者須要獲取數據的時候,就能夠經過這個引用來獲取了
關於兩種模型的比較兩種實現模型,在開發的時候,究竟應該使用哪種,仍是應該具體問題具體分析。這裏,只是把兩種模型進行一個簡單的比較。
1:推模型是假定目標對象知道觀察者須要的數據;而拉模型是目標對象不知道觀察者具體須要什麼數據,沒有辦法的狀況下,乾脆把自身傳給觀察者,讓觀察者本身去按需取值。
2:推模型可能會使得觀察者對象難以複用,由於觀察者定義的update方法是按需而定義的,可能沒法兼顧沒有考慮到的使用狀況。這就意味着出現新狀況的時候,就可能須要提供新的update方法,或者是乾脆從新實現觀察者。 而拉模型就不會形成這樣的狀況,由於拉模型下,update方法的參數是目標對象自己,這基本上是目標對象能傳遞的最大數據集合了,基本上能夠適應各類狀況的須要。
Java中的觀察者模式在java.util包裏面有一個類Observable,它實現了大部分咱們須要的目標的功能;還有一個接口Observer,它裏面定義了update的方法,就是觀察者的接口。
1:觀察者模式實現了觀察者和目標之間的抽象耦合
2:觀察者模式實現了動態聯動
3:觀察者模式支持廣播通訊
4:觀察者模式可能會引發無謂的操做
觀察者模式的本質是:觸發聯動
1:當一個抽象模型有兩個方面,其中一個方面的操做依賴於另外一個方面的狀態變化,那麼就能夠選用觀察者模式。
2:若是在更改一個對象的時候,須要同時連帶改變其它的對象,並且不知道究竟應該有多少對象須要被連帶改變,這種狀況能夠選用觀察者模式,被更改的那一個對象很明顯就至關因而目標對象,而須要連帶修改的多個其它對象,就做爲多個觀察者對象了。
3:當一個對象必須通知其它的對象,可是你又但願這個對象和其它被它通知的對象是鬆散耦合的,也就是說這個對象其實不想知道具體被通知的對象,這種狀況能夠選用觀察者模式,這個對象就至關因而目標對象,而被它通知的對象就是觀察者對象了。
1:範例需求這是一個實際系統的簡化需求:在一個水質監測系統中有這樣一個功能,當水中的雜質爲正常的時候,只是通知監測人員作記錄;當爲輕度污染的時候,除了通知監測人員作記錄外,還要通知預警人員,判斷是否須要預警;當爲中度或者高度污染的時候,除了通知監測人員作記錄外,還要通知預警人員,判斷是否須要預警,同時還要通知監測部門領導作相應的處理。
2:解決思路和範例代碼分析上述需求就會發現,對於水質污染這件事情,有可能會涉及到監測員、預警人員、監測部門領導,根據不一樣的水質污染狀況涉及到不一樣的人員,也就是說,監測員、預警人員、監測部門領導他們三者是平行的,職責都是處理水質污染,可是處理的範圍不同。所以很容易套用上觀察者模式,若是把水質污染的記錄看成被觀察的目標的話,那麼監測員、預警人員和監測部門領導就都是觀察者了。
前面學過的觀察者模式,當目標通知觀察者的時候是所有都通知,可是如今這個需求是不一樣的狀況來讓不一樣的人處理,怎麼辦呢?解決的方式一般有兩種,一種是目標能夠通知,可是觀察者不作任何操做;另一種是在目標裏面進行判斷,乾脆就不通知了。兩種實現方式各有千秋,這裏選擇後面一種方式來示例,這種方式可以統一邏輯控制,並進行觀察者的統一分派,有利於業務控制和從此的擴展。