10.觀察者模式-Observer

定義

​ 定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於它的對象都獲得通知並被自動更新。也叫訂閱者模式。java

結構和說明

Subject:大數據

  目標對象,一般具備以下功能:spa

  (1)一個目標能夠被多個觀察者觀察server

  (2)目標提供對觀察者註冊和退訂的維護對象

  (3)當目標的狀態發生變化時,目標負責通知全部註冊的、有效的觀察者blog

Observer:接口

  定義觀察者的接口,提供目標通知時對應的更新方法,這個更新方法進行相應的業務處理,能夠在這個方法裏面回調目標對象,以獲取目標對象的數據。開發

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:觀察者模式的調用順序示意圖在使用觀察者模式時,會很明顯的分紅兩個階段,第一個階段是準備階段,也就是維護目標和觀察者關係的階段,這個階段的調用順序如圖

 

接下來就是實際的運行階段了,這個階段的調用順序如圖

8:通知的順序從理論上說,當目標對象的狀態變化後通知全部觀察者的時候,順序是不肯定的,所以觀察者實現的功能,絕對不要依賴於通知的順序,也就是說,多個觀察者之間的功能是平行的,相互不該該有前後的依賴關係

推模型和拉模型

推模型:

  目標對象主動向觀察者推送目標的詳細信息,無論觀察者是否須要,推送的信息一般是目標對象的所有或部分數據,至關因而在廣播通訊。

拉模型:

  目標對象在通知觀察者的時候,只傳遞少許信息,若是觀察者須要更具體的信息,由觀察者主動到目標對象中獲取,至關因而觀察者從目標對象中拉數據。通常這種模型的實現中,會把目標對象自身經過update方法傳遞給觀察者,這樣在觀察者須要獲取數據的時候,就能夠經過這個引用來獲取了

  關於兩種模型的比較兩種實現模型,在開發的時候,究竟應該使用哪種,仍是應該具體問題具體分析。這裏,只是把兩種模型進行一個簡單的比較。

  1:推模型是假定目標對象知道觀察者須要的數據;而拉模型是目標對象不知道觀察者具體須要什麼數據,沒有辦法的狀況下,乾脆把自身傳給觀察者,讓觀察者本身去按需取值

  2:推模型可能會使得觀察者對象難以複用,由於觀察者定義的update方法是按需而定義的,可能沒法兼顧沒有考慮到的使用狀況。這就意味着出現新狀況的時候,就可能須要提供新的update方法,或者是乾脆從新實現觀察者。​ 而拉模型就不會形成這樣的狀況,由於拉模型下,update方法的參數是目標對象自己,這基本上是目標對象能傳遞的最大數據集合了,基本上能夠適應各類狀況的須要。

Java中的觀察者模式在java.util包裏面有一個類Observable,它實現了大部分咱們須要的目標的功能;還有一個接口Observer,它裏面定義了update的方法,就是觀察者的接口。

觀察者模式的優缺點

1:觀察者模式實現了觀察者和目標之間的抽象耦合

2:觀察者模式實現了動態聯動

3:觀察者模式支持廣播通訊

4:觀察者模式可能會引發無謂的操做

思考觀察者模式

觀察者模式的本質

觀察者模式的本質是:觸發聯動

什麼時候選用觀察者模式

1:當一個抽象模型有兩個方面,其中一個方面的操做依賴於另外一個方面的狀態變化,那麼就能夠選用觀察者模式。

2:若是在更改一個對象的時候,須要同時連帶改變其它的對象,並且不知道究竟應該有多少對象須要被連帶改變,這種狀況能夠選用觀察者模式,被更改的那一個對象很明顯就至關因而目標對象,而須要連帶修改的多個其它對象,就做爲多個觀察者對象了。

3:當一個對象必須通知其它的對象,可是你又但願這個對象和其它被它通知的對象是鬆散耦合的,也就是說這個對象其實不想知道具體被通知的對象,這種狀況能夠選用觀察者模式,這個對象就至關因而目標對象,而被它通知的對象就是觀察者對象了。

簡單變形示例——區別對待觀察者

1:範例需求這是一個實際系統的簡化需求:在一個水質監測系統中有這樣一個功能,當水中的雜質爲正常的時候,只是通知監測人員作記錄;當爲輕度污染的時候,除了通知監測人員作記錄外,還要通知預警人員,判斷是否須要預警;當爲中度或者高度污染的時候,除了通知監測人員作記錄外,還要通知預警人員,判斷是否須要預警,同時還要通知監測部門領導作相應的處理。

2:解決思路和範例代碼分析上述需求就會發現,對於水質污染這件事情,有可能會涉及到監測員、預警人員、監測部門領導,根據不一樣的水質污染狀況涉及到不一樣的人員,也就是說,監測員、預警人員、監測部門領導他們三者是平行的,職責都是處理水質污染,可是處理的範圍不同。所以很容易套用上觀察者模式,若是把水質污染的記錄看成被觀察的目標的話,那麼監測員、預警人員和監測部門領導就都是觀察者了。

前面學過的觀察者模式,當目標通知觀察者的時候是所有都通知,可是如今這個需求是不一樣的狀況來讓不一樣的人處理,怎麼辦呢?解決的方式一般有兩種,一種是目標能夠通知,可是觀察者不作任何操做;另一種是在目標裏面進行判斷,乾脆就不通知了。兩種實現方式各有千秋,這裏選擇後面一種方式來示例,這種方式可以統一邏輯控制,並進行觀察者的統一分派,有利於業務控制和從此的擴展。

相關文章
相關標籤/搜索