認識觀察者模式java
咱們看看報紙和雜誌的訂閱是怎麼回事:編程
1、報社的業務就是出版報紙測試
2、向某家報社訂閱報紙,只要他們有新報紙出版,就會給你送來。只要你是他們的訂戶,你就會一直收到新報紙spa
3、當你不想再看報紙的時候,取消訂閱,他們就不會再送新報紙來設計
4、只要報社還在運營,就會一直有人(或單位)向他們訂閱報紙或取消訂閱報紙orm
出版者+訂閱者=觀察者模式server
若是你瞭解報紙的訂閱是怎麼回事,其實就知道觀察者模式是怎麼回事,只是名稱不太同樣:出版者改稱爲「主題」(Subject),訂閱者改稱爲"觀察者"(Observer)對象
1、主題對象管理某些數據繼承
2、當主題內的數據改變就會通知觀察者接口
3、一旦數據改變,新的數據會以某種形式送到觀察者手上
4、觀察者訂閱(註冊)主題以便在主題數據改變時可以收到更新
5、某個對象不是觀察者,因此在主題數據改變使不會被通知
觀察者的一天
1、鴨子對象過來告訴主題,它至關一個觀察者
2、鴨子其實想說的是:我對你的數據改變感興趣,一有變化請通知我
3、鴨子對象如今已是正式的觀察者了
4、鴨子靜候通知,等待參與這項偉大的事情。一旦接獲通知,就會獲得一個整數
5、主題有了新的數據值
6、如今鴨子和其餘全部觀察者都會收到通知:主題已經改變了
7、老鼠對象要求從觀察者中把本身除名
8、老鼠已經觀察此主題過久,厭倦了,因此決定再也不當個觀察者
9、老鼠離開了
10、主題知道老鼠的請求以後,把它從觀察者中除名
11、主題有一個新的整數
12、除了老鼠以外,每一個觀察者都會收到通知,由於它已經被除名了。噓!不要告訴別人,老鼠其實心中暗暗地懷念這個整數,或許哪天又會再次註冊,回來繼續當觀察者
定義觀察者模式
1、當你試圖勾勒觀察者模式時,能夠利用報紙訂閱服務,以及出版者和訂閱者比擬這一切
2、在真實的世界中,你一般會看到觀察者模式被定義成
3、觀察者模式——定義了對象之間的一對多依賴,這樣依賴,當一個對象改變狀態時,它的全部依賴者都會收到通知並自動更新
4、主題和觀察者定義了一對多的關係,觀察者依賴於此主題,只要主題狀態一有變化,觀察者就會被通知。根據通知的風格,觀察者可能所以新值而更新
5、實現觀察者模式的方法不僅一種,可是以包含Subject與Observer接口的類設計的作法最多見
定義觀察者模式:類圖
1、這是主題接口,對象使用此接口註冊爲觀察者,或者把本身從觀察者中刪除
2、每一個主題能夠有許多觀察者
3、全部潛在的觀察者必須實現觀察者接口,這個接口只有update()一個方法,當主題狀態改變時它被調用
4、一個具體主題老是實現主題接口,除了註冊和撤銷方法以外,具體主題還實現了notifyObservers()方法,此方法用於在狀態改變時更新全部當前觀察者
5、具體主題也可能有設置和獲取狀態的方法
6、具體的觀察者能夠是實現此接口的任意類。觀察者必須註冊具體主題,以便接收更新
這和一對多的關係有何關聯?
利用觀察者模式,主題是具備狀態的對象,而且能夠控制這些狀態。也就是說,有「一個」具備狀態的主題。另外一方面,觀察者使用這些狀態,雖然這些狀態並不屬於它們。、有許多的觀察者,依賴主題告訴它們狀態什麼時候改變了。這就產生一個關係:"一個"主題對「多個」觀察者的關係
其間的依賴是如何產生的?
由於主題是真正擁有數據的人,觀察者是主題的依賴者,在數據變化時更新,這樣比起讓許多對象控制同一份數據來,能夠獲得更乾淨的OO設計
鬆耦合的威力
1、當兩個對象之間鬆耦合,它們依賴能夠交互,可是不太清楚彼此的細節
2、觀察者模式提供了一種對象設計,讓主題和觀察者之間鬆耦合
爲何?
1、關於觀察者的一切,主題只知道觀察者實現了某一個接口(也就是Observer接口)。主題不須要知道觀察者的具體類是誰,作了些什麼或其餘任何細節
2、任什麼時候候咱們均可以增長新的觀察者。由於主題惟一依賴的東西是一個實現Observer接口的對象列表,因此咱們能夠隨時增長觀察者。事實上,在運行時咱們能夠用新的觀察者取代現有的觀察者,主題不會收到任何影響。一樣的,也能夠在任什麼時候候刪除某些觀察者
3、有新類型的觀察者出現時,主題的代碼不須要修改。假如咱們有個新的具體類須要當觀察者,咱們不須要爲了兼容新類型而修改主題的代碼,全部要作的就是在新的類裏實現此觀察者接口,而後註冊爲觀察者便可。主題不在意別的,它只會發送通知給全部實現了觀察者接口的對象
4、咱們能夠獨立地複用主題或觀察者。若是咱們在其餘地方須要使用主題和觀察者,能夠輕易地複用,由於兩者並不是緊耦合
5、改變主題或觀察者其中一方,並不會影響另外一方。由於二者是鬆耦合的,因此只要他們之間的接口仍被遵循,咱們就能夠自由地改變他們
6、設計原則——爲了交互對象之間得鬆耦合設計而努力
7、鬆耦合的設計之因此能讓咱們創建有彈性的OO系統,可以應對變化,是由於對象之間的互相依賴降到了最低
把觀測值直接傳入觀察者中是更新狀態的最直接的方法,你認爲這樣的作法明智嗎?
這些觀測值的種類和個數在將來有可能改變嗎?若是之後會改變,這些變化是否被很好地封裝?或者是須要修改許多代碼才能辦到?
關於將更新的狀態傳送給觀察者,你可否想到更好的方法解決此問題?
圍爐夜話:主題與觀察者
主題:我很高興,咱們終於有機會面對面聊天了
觀察者:是這樣嗎?我覺得你根本不在意咱們這羣觀察者呢?
主題:哎呀!我把該作的事都作到了,不是嗎?我老是會通知大家發生什麼事了....我雖然不知道大家是誰,但這不意味着我不在意大家。何況,我知道關於大家的一件重要的事:大家實現了Observer接口
觀察者:是呀,但只是關於個人一小部分罷了!不管如何,我對你更瞭解
主題:是嗎?說來聽聽!
觀察者:嗯!你老是將你的狀態傳給咱們,因此咱們能夠知道你內部的狀況。有時候,這很煩人的.....
主題:拜託,我必須主動送出個人狀態和通知給你們,好讓大家這些懶惰的觀察者知道發生什麼事了
觀察者:咳!等等,我說主題先生,首先,咱們並不懶,在你那些"很重要"、通知的空檔中,咱們還有別的事要作。另外,爲什麼由你主動送數據過來,而不是讓咱們主動去向你索取數據?
主題:嗯——這樣或許也行,只是我必須所以門戶大開,讓大家所有均可以進來取得大家須要的狀態,這樣太危險了。我不能讓大家進來裏面大肆挖掘個人各類數據
觀察者:你何不提供一些公開的getter方法,讓咱們「拉」走咱們須要的狀態?
主題:是的,我可讓大家「拉」走個人狀態,可是你不以爲這樣大家反而不方便嗎?若是每次想要數據時都來找我,你可能要調用不少次才能收集齊全你所要的狀態。這就是爲何我更喜歡"推的緣由,大家能夠在一次通知中一口氣獲得全部東西
觀察者:死鴨子嘴硬!觀察者種類這麼多,你不可能事先料到咱們每一個人的需求,仍是讓咱們直接去取得咱們須要的狀態比較恰當,這樣一來,若是咱們有人只須要一點點數據,就不會被強迫收到一堆數據。這麼作同時也能夠在之後比較容易修改。比方說,哪一天你決定擴展功能,新增更多的狀態,若是採用我建議的方式,你就不用修改和更新對每位觀察者的調用,只需改變本身來容許更多的getter方法來取得新增的狀態
主題:是的,兩種作法都有各自的優勢。我注意到Java內置的Observer模式兩種作法都支持
觀察者:真的嗎?
主題:太好了,或許我會看到一個"拉"的好例子,於是改變個人想法
使用Java內置的觀察者模式
到目前爲止,咱們已經從無到有地完成了觀察者模式,可是,Java API有內置得觀察者模式。java.util包(package)內包含最基本的Observer接口與Observable類,這和咱們的Subject接口與Observer接口很類似Observer接口與Observalbe類使用上更方便,由於許多功能都已經事先準備好了。你甚至可使用推(push)或拉(pull)的方式傳送數據,稍後就會看到這樣的例子。
有了Java內置的支持,你只須要擴展(繼承)Observable,並告訴它什麼時候該通知觀察者,一切就完成了,剩下的事API會幫你作
setChanged()方法
setChanged()方法用來標記狀態已經改變的事實,好讓notifyObservers()知道當它被調用時應該更新觀察者。若是調用notifyObservers()以前沒有先調用setChanged(),觀察者就「不會」被通知
1、setChanged()方法把changed標誌設爲true
2、notifyObservers()只會在changed標爲"true"時通知觀察者
3、在通知觀察者以後,把changed標誌社回false
這樣作有其必要性,setChanged()方法可讓你在更新觀察者時,有更多的彈性,你能夠更適當地通知觀察者。比方說,若是沒有setChanged()方法,咱們得氣象站測試是如此敏銳,以致於溫度計讀書每十分之一度就會更新,這會形成WeatherData對象持續不斷地通知觀察者,咱們並不但願看到這樣的事情發生。若是咱們但願半度以上才更新,就能夠在溫度差距到達半度時,調用setChanged(),進行有效的更新。
你也許不會常常用到此功能,可是把這樣的功能準備好,當須要時立刻就可使用。總之,你須要調用setChanged(),以便通知開始運轉。若是此功能在某些地方對你有幫助,你可能也須要clearChanged()方法,將changed狀態設置回false。另外也有一個hasChanged()方法,告訴你changed標誌的當前狀態
不要依賴於觀察者被通知的次序
1、java.util.Observable實現了它的notifyObservers()方法,這致使了通知觀察者的次序不一樣於咱們先前的次序。誰也沒有錯,只是雙方選擇不一樣的方式實現罷了
2、可是能夠肯定的是,若是咱們的代碼依賴這樣的次序,就是錯的。爲何呢?由於一旦觀察者/可觀察者的實現有所改變,通知次序就會改變,極可能就會產生錯誤的結果,這絕對不是咱們所認爲的鬆耦合
難道Java.util.Observable違反了咱們的OO設計原則:針對接口編程,而非針對實現編程?
java.util.Observable的黑暗面
1、是的,可觀察者是一個「類」而不是一個「接口」,更糟糕的是,它甚至沒有實現一個接口。不幸的是,java.util.Observable的實現有許多問題,限制了它的使用和複用。這並非說它沒有提供有用的功能,咱們只是想提醒你們注意一些事實
2、Observable是一個類,你已經從咱們的原則得知這不是一件好事,可是,這到底會形成什麼問題呢?
3、首先,由於Observable是一個「類」,你必須設計一個類繼承它。若是某類想同時具備Observable類和另外一個超類的行爲,就會陷入兩難,畢竟Java不支持多重繼承,這限制了Observable的複用潛力(而增長複用潛力不正是咱們使用模式最原始的動機嗎?)
4、再者,由於Observable接口,因此你沒法創建本身的實現,和Java內置的ObserverAPI搭配使用,也沒法將java.util的實現換成另外一套作法的實現(比方說,Observable將關鍵的方法保護起來,若是你看看ObservableAPI,你會發現setChanged()方法被保護起來了(被定義成protected),那又怎麼樣呢?這意味着:除非你繼承自Observable,不然你沒法建立Observable實例並組合到你本身的對象中來,這個設計違反了第二設計原則:"多用組合,少用繼承")
5、若是你可以擴展java.util.Observable,那麼Observable"可能"能夠符合你的需求,不然,你可能須要像以前的方式本身實現這一整套觀察者模式,無論用哪一種方法,反正你都已經熟悉觀察者模式了
在JDK中,還有哪些地方能夠找到觀察者模式
在JDK中,並不是只有在java.util中次啊能找到觀察者模式,其實在JavaBeans和Swing中,也都實現了觀察者模式。
OO原則
1、封裝變化
2、多用組合,少用繼承
3、針對接口編程,不針對實現編程
四、爲交互對象之間的鬆耦合設計而努力
OO模式
觀察者模式(Observer pattern)——在對象之間定義一對多的依賴,這樣一來,當一個對象改變狀態,依賴它的對象都會收到通知,並自動更新
要點
1、觀察者模式定義了對象之間一對多的關係
二、主題(也就是可觀察者)用一個共同的接口來更新觀察者
三、觀察者和可觀察者之間用鬆耦合方式結合(loosecoupling),可觀察值不知道觀察者的細節,只知道觀察者實現了觀察者接口
四、使用此模式時,你可從被觀察者處推(push)或拉(pull)數據(然而,推的方式被認爲更「正確」)
五、有多個觀察者時,不能夠依賴特定的通知次序
六、Java有多種觀察者模式的實現,包括了通用的java.util.Observable
七、要注意java.util.Observable實現上所帶來的一些問題
八、若是有必要的話,能夠實現本身的Observable,這並不難,不要懼怕
九、Swing大量使用觀察者模式,許多GUI也是如此
十、此模式也被應用在許多地方,例如:JavaBeans、RMI
觀察者的表明人物——MVC
觀察者模式包含的OO設計原則
1、在觀察者模式中,會改變的是主題的狀態,以及觀察者的數目和類型。用這個模式,你能夠改變依賴於主題狀態的對象,卻沒必要改變主題,這就叫提早規劃
2、主題與觀察者都使用接口:觀察者利用主題的接口向主題註冊,而主題利用觀察者接口通知觀察者,這樣可讓二者之間運做正常,又同時具備鬆耦合的優勢
3、觀察者模式利用「組合」將許多觀察者組合進主題中,對象之間的這種關係不是經過繼承產生的,而是在運行時利用組合的方式而產生的