觀察者模式:在對象之間定義一對多的依賴,這樣一來,當一個對象改變狀態,依賴它的對象都會收到通知,並自動更新。 舉個例子:氣象監測應用 此系統的三個部分是氣象站(獲取實際氣象數據的物理裝置)、WeatherData對象(追蹤來自氣象站的數據,並更新佈告板)和佈告板(有三個佈告板,一個用來顯示目前天氣狀 ...
觀察者模式:在對象之間定義一對多的依賴,這樣一來,當一個對象改變狀態,依賴它的對象都會收到通知,並自動更新。
舉個例子:氣象監測應用
此系統的三個部分是氣象站(獲取實際氣象數據的物理裝置)、WeatherData對象(追蹤來自氣象站的數據,並更新佈告板)和佈告板(有三個佈告板,一個用來顯示目前天氣狀況,一個顯示氣象統計,最後一個用來顯示天氣預報)
看一下WeatherData源文件:
上面的三個get各自返回最近的氣象測量數據(分別為:溫度、濕度、氣壓),我們不在乎這些變數如何通過氣象站這個物理裝置得到以及它們如何設置,WeatherData對象自己知道如何從氣象站獲取更新信息。
measurementsChanged() {/* 一旦氣象測量更新,此方法會調用,這裡面寫自己的代碼 */}
先看一個錯誤示範:
public class WeatherData{ //實例變數聲明 public void measurementsChanged() { //調用get函數,獲取最近的測量值 float temp = getTemperature(); float humidity = getHumidity(); float pressure = getPressure(); //更新現在佈告板、氣象統計佈告板、天氣預報佈告板 currentConditionsDisplay.update(temp,humidity,pressure); statisticsDisplay.update(temp,humidity,pressure); forecastDisplay.update(temp,humidity,pressure); } //這裡是其他的WeatherData方法 }
問題出在哪裡?為什麼這樣是錯誤的?
currentConditionsDisplay.update(temp,humidity,pressure); statisticsDisplay.update(temp,humidity,pressure); forecastDisplay.update(temp,humidity,pressure); /* 以上屬於具體實現編程,會導致我們以後在增加或刪除佈告板時必須修改程式*/ /* 另外上面的代碼屬於改變的部分,因此需要封裝獨立出來 */
在繼續向下走之前,先看一個觀察者模式的實例
尋找工作的軟體開發人員將自己的聯繫方式留給獵頭,然後當獵頭獲取工作應聘消息時需要通知他手裡名單上的所有人員。
但是也有這樣的情況,當軟體開發人員沒有向獵頭提交信息時,獵頭在收到消息後不同通知他。
同樣接受獵頭通知的求職人員同樣可以取消通過獵頭來尋求工作,比如軟體開發人員2取消了這種求職方式,那麼他就不會再收到獵頭的通知。
但是軟體開發人員5希望通過這種方式來找工作,那麼就需要去和獵頭聯繫並留下自己的聯繫方式,那麼從此軟體開發人員5也可以收到通知了
在明白上面的例子之後,重新回到我們的氣象監測應用上。
嘗試著畫出實現氣象站所需要的類,其中包括WeatherData類以及佈告欄組件。確定你的圖像能夠顯示出各個部分如何結合起來,以及別的開發人員如何能夠實現自己的佈告欄組件。(每個佈告欄都應該有一個被命名為“subject(主題)”的指針指向WeatherData對象,為了避免混亂下麵沒有畫出)
實現氣象站:
從建立介面開始
public interface Subject{ public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObservers(); } public interface Observer{ public void update(float temp,float humidity,float pressure); } public interface DisplayElement{ public void display(); }
在WeatherData中實現主題介面
public class WeatherData implements Subject{//WeatherData實現了Subject介面 private ArrayList observers;//添加一個ArrayList來記錄觀察者,此ArrayList是在構造器中建立的 private float temperature; private float humidity; private float pressure; public WeatherData(){ observers = new ArrayList(); } public void registerObserver(Observer o){//當註冊觀察者時,我們只要把它加到ArrayList後面即可 observers.add(o); } public void removeObserver(Observer o){//同樣刪除時,得到索引然後從ArrayList刪除即可 int i = observers.indexOf(o); if(i >= 0){ observers.remove(i); } } public void notifyObservers(){//這裡我們把狀態都告訴每一個觀察者,因為觀察者都實現了update(), for(int i = 0 ;i < observers.size(); i++){ Observer observer = (Observer)observers.get(i); observer.update(temperature,humidity,pressure); } } public void measurementsChanged(){//當氣象站更新數據時,我們通知觀察者 notifyObservers(); } public void setMeasurements(float temperature,float humidity,float pressure){ this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } }
下麵只給出了目前狀況的佈告板:
//此佈告板實現了Observer介面,所以可以從WeatherData對象中獲取改變,同樣也實現了DisplayElement的介面 public class CurrentConditionsDisplay implements Observer,DisplayElement{ private float temperature; private float humidity; private Subject weatherData; public CurrentConditionsDisplay(Subject weatherData){//構造器需要weatherData對象 作為註冊使用 this.weatherData = weatherData; weatherData.registerObserver(this); } public void update(float temperature,float humidity,float pressure){ this.temperature = temperature; this.humidity = humidity;//當update()調用時,我們把溫度和濕度保存起來,然後調用display() display();//這裡之前說的可能不清楚,目前狀況只需要顯示溫度和濕度就可以了, //氣象檢測系統不要求在這個佈告板顯示氣壓 } public void display(){ System.out.println("Current conditions:"+temperature +"F degrees and "+ humidity +" % humidity"); } }
下麵討論使用Java內置的觀察者模式來完成氣象監測應用
在java.util包中包含最基本的Observer介面與Observable類。甚至可以實現推(push)和拉(pull)方式來傳送數據。什麼叫做push,什麼叫做pull呢?
還用之前的例子,獵頭主動將信息通知給每一位軟體開發人員叫做push,那麼至於拉(pull)就很直觀了,就是軟體開發人員主動向獵頭聯繫索取信息。(每個佈告欄都應該有一個被命名為“subject(主題)”的指針指向WeatherData對象,為了避免混亂下麵沒有畫出)
在這裡需要註意的地方:
在Observable裡面有一個setChanged()方法,它的作用是在調用notifyObserver()之前先調用setChanged(),如果changed為true那麼就進行通知,否則就不通知。(setChanged()在每次消息有所改變時都要進行修改)
看一下裡面的代碼:
setChanged(){ changed= true;//將changed標誌設為true } notifyObservers(Object arg){ if(changed){//只有在是true是才通知 for every observer on the list{ call update(this.arg); } changed=false;//通知完觀察者之後,把changed標誌設回false } } notifyObservers(){ notifyObservers(null); }
1.Observable是一個類,首先既然它是個類,那麼我們需要設計一個類來繼承它。但是Java不支持多重繼承,這就限制了Observable的服用能力。在再者因為沒有Observable介面,所以自己無法創建自己的實現來和Java內置的Observer API搭配使用,也無法將java.util的實現換成另一套做法的實現。
2.Observable將關鍵的方法保護起來,如果查看Observable API則會發現,setChanged()方法被保護了起來(被定義為protected)。這就意味著:除非你繼承自Observable,否則你無法創建Observable實例並組合到你自己的對象中來。這違背了設計原則“多用組合,少用繼承”
到此我們現在的設計模式已經學習了三個——簡單工廠模式、策略模式以及現在的觀察者模式。
未來的路還很遠,加油吶!