Head First 設計模式總結(二)觀察者模式
本文對《Head First 設計模式》中的觀察者模式進行了概括和總結
觀察者模式——在物件之間定義一對多的依賴,這樣一來,當一個物件改變狀態,依賴他的物件都會收到通知,並自動更新。
問題描述
要求公司建立一個 氣象資料站 (WeatherData物件),使之能從 氣象觀測站 (WeatherStation物件)獲取氣象資料(溫度temperature,溼度humidity,氣壓值pressure)。
並要求公司開發一個應用,使其包含三個天氣顯示板:目前狀況、氣象統計、天氣預測,這三個物件都獲取了來自WeatherData的三個氣象資料,但是對這些資料的處理方式不一樣。要求顯示板的顯示資料根據WeatherData傳過來的氣象資料實時變化。 下圖就是將氣象資料傳給了“目前狀況”這個展示板。
下面進入正題。
觀察者模式的思想
主題(Subject)+觀察者(Observer)=觀察者模式
下圖是Subject和Observer之間的關係 觀察者模式提供了一種物件設計,讓主題和觀察者之間鬆耦合。 關於觀察者的一切,主題只知道觀察者實現了某個介面(Observer介面),主題只需要知道這個觀察者 註冊與否 (是否需要定期向它提供訊息),如果註冊了,就定期傳送訊息給他。主題不需要知道觀察者本身具體是誰、做了什麼以及其他細節。
當新的觀察者註冊時,不用改主題程式碼,只需將通知的訊息給這個新來的觀察者發一份就行了,觀察者不在乎別的,它只會將相同訊息傳送給所有實現了Observer介面的具體觀察者物件,它一視同仁
以下是本例利用觀察者模式的結構圖
下面給出本例WeatherData類的實現程式碼
class WeatherData implements Subject { private float temperature; private float humidity; private float pressure; private ArrayList observers; public WeatherData(){ List<Observer> observers = new ArrayList<Observer>(); } @Override public void registerObserver(Observer o) { //註冊為觀察者 observers.add(o); } @Override public void removeObserver(Observer o) { //登出,不再為觀察者 int i = observers.indexOf(o); if(i>=0){ observers.remove(o); } } @Override public void notifyObservers() { //通知各個觀察者 for(int i = 0;i<observers.size();i++){ //遍歷觀察者List,挨個呼叫每個觀察者的update()方法,實現通知 Observer observer = (Observer) observers.get(i); observer.update(temperature,humidity,pressure); } } public void measurementsChanged(){ notifyObservers(); } public void setMeasurements(float temp,float humidity,float pressure){ this.temperature = temp; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); WeatherStation.times++; } }
以下是其中一個具體觀察者的程式碼實現:
class CurrentConditionDisplay implements Observer{
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionDisplay(Subject weatherData){ //通過觀察者的構造器將weatherData傳進來,並將該觀察者註冊
this.weatherData = weatherData;
weatherData.registerObserver(this);
//weatherData.removeObserver(this);
}
public void display() {
System.out.println("current condition: "+temperature+"F degrees and"+humidity+"% humidity");
}
@Override
public void update(float temp, float humidity, float pressure) { //該方法由weatherData的notifyObserver()方法觸發
this.temperature = temp;
this.humidity = humidity;
display();
}
}
總結
該設計模式的關鍵在於:如何將具體主題(WeatherData)和具體觀察者(Concret Observer)相互聯絡起來
解析:
1、觀察者們利用weatherData物件去註冊和登出
registerObserver()方法和removeObserver()方法都在weatherData物件中,因此具體觀察者的註冊必須通過weatherData物件來完成,但是是否註冊還是由具體觀察者它自己決定,所以得將weatherData物件傳到具體觀察者內部,顯然直接將weatherData物件傳入具體觀察者的構造器最直觀,因為當new出具體觀察者物件的時刻就能決定是否註冊為觀察者了。
2、weatherData利用觀察者們的物件才能呼叫它們的update()方法
由於有多個觀察者,因此可用一個Observer型別的List來存放註冊了的Observer,一旦某個Observer註冊成功,該觀察者的物件就傳到了List中,因此weatherData可以訪問所有註冊過的觀察者。這樣的目的在這裡很明確,一旦新資料到來,weatherData物件就呼叫notifyObserver()方法,進而利用List中的所有觀察者物件去呼叫它們各自的update()方法。