設(shè)計模式學(xué)習(xí)專欄三--------觀察者模式
場景
建立一個氣象站應(yīng)用, 利用WeatherData對象取得數(shù)據(jù),并更新三個布告板: 目前狀況 , 氣象統(tǒng)計 , 天氣預(yù)報
此系統(tǒng)中的三個部分是氣象站(獲取實際氣象數(shù)據(jù)的物理裝置) , WeatherData對象(追蹤來自氣象站的數(shù)據(jù),并更新布告板) 和 布告板(顯示目前天氣狀況給用戶看.)
WeatherData對象知道如何跟物理氣象站聯(lián)系,以取得更新的數(shù)據(jù).WeatherData對象會隨即更新三個布告板的顯示: 目前狀況(溫度 , 濕度 , 氣壓), 氣象統(tǒng)計和天氣預(yù)報
剛開始實現(xiàn)方式
//WeatherData構(gòu)造函數(shù) , 當(dāng)新公告板加入時, 也要跟著改變
public WeatherData(CurrentConditionsDisplay currentConditionsDisplay,
StatisticsDisplay statisticsDisplay,ForecastDisplay forecastDisplay) {
this.currentConditionsDisplay = currentConditionsDisplay;
this.statisticsDisplay = statisticsDisplay;
this.forecastDisplay = forecastDisplay;
}
如何解決
分析可變部分
在weatherData的構(gòu)造函數(shù)中 , 對布告板的加入與移除
在收到氣象數(shù)據(jù)變化時, 需要通知所有已加入的布告板
觀察者模式總覽
定義:在
對象之間定義一對多的依賴
,這樣一來, 當(dāng)一個對象改變狀態(tài),依賴它的對象都會收到通知,并自動更新
-
類圖
-
模式的理解
-
角色
- 主題Subject : 主題對象
管理某些數(shù)據(jù)
, 當(dāng)主題內(nèi)的數(shù)據(jù)改變時 , 就會通知觀察者
- 觀察者Observer : 觀察者已經(jīng)
訂閱
主題, 以便在主題數(shù)據(jù)改變時能收到更新 , 當(dāng)不想訂閱主題時,也能隨時移除訂閱
- 主題Subject : 主題對象
-
細(xì)節(jié)
使用觀察者模式在任意時候都能 增加/刪除新的觀察者,而不修改原有代碼
-
觀察者模式 對數(shù)據(jù)推送有兩種方式
推(push)和拉(pull)
- 推送方式:
- 當(dāng)主題通知變化時, 主題攜帶著數(shù)據(jù)通知觀察者.
- 拉取方式
- 當(dāng)主題通知變化時, 觀察者通過主題的引用 , 去獲取主題中的數(shù)據(jù)信息
- 推送方式:
-
觀察者模式提供了一種對象設(shè)計,
讓主題和觀察者之間松耦合
- 對主題而言, 它只知道觀察者實現(xiàn)了某個接口(Observer). 而不需要知道觀察者的具體實現(xiàn)是誰, 做了些什么與其他細(xì)節(jié)
- 對觀察者而言, 它不需要關(guān)心主題細(xì)節(jié), 只知道可以注冊/移除主題 , 并且主題有數(shù)據(jù)更新時會調(diào)用update方法傳入數(shù)據(jù)
-
核心代碼部分
-
主題
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); }
-
WeatherData
public class WeatherData implements Subject { private ArrayList<Observer> observers; private float temperature; private float humidity; private float pressure; public WeatherData() { observers = new ArrayList<Observer>(); } public void registerObserver(Observer o) { observers.add(o); } public void removeObserver(Observer o) { int i = observers.indexOf(o); if (i >= 0) { observers.remove(i); } } public void notifyObservers() { //遍歷所有的觀察者,調(diào)用update方法 for (Observer observer : observers) { observer.update(temperature, humidity, pressure); } } public void measurementsChanged() { notifyObservers(); } //氣象站數(shù)據(jù)改變時調(diào)用的方法 public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } //其他的get方法 }
-
觀察者--今日布告板
public class CurrentConditionsDisplay implements Observer, DisplayElement { private float temperature; private float humidity; private Subject weatherData; //持有主題引用 , 用于拉取數(shù)據(jù)/刪除主題 public CurrentConditionsDisplay(Subject weatherData) { this.weatherData = weatherData; weatherData.registerObserver(this); } public void update(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; display(); } public void display() { System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity"); } }
-
主程序 ----主題數(shù)據(jù)改變
public class WeatherStationHeatIndex { public static void main(String[] args) { WeatherData weatherData = new WeatherData(); CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData); StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData); ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData); weatherData.setMeasurements(80, 65, 30.4f); weatherData.setMeasurements(82, 70, 29.2f); weatherData.setMeasurements(78, 90, 29.2f); } }
-
輸出結(jié)果
Current conditions: 80.0F degrees and 65.0% humidity Avg/Max/Min temperature = 80.0/80.0/80.0 Forecast: Improving weather on the way! Current conditions: 82.0F degrees and 70.0% humidity Avg/Max/Min temperature = 81.0/82.0/80.0 Forecast: Watch out for cooler, rainy weather Current conditions: 78.0F degrees and 90.0% humidity Avg/Max/Min temperature = 80.0/82.0/78.0 Forecast: More of the same
Java內(nèi)置的觀察者模式
主題 ==> java.util.Observable
觀察者 ==>
java.util.Observer
-
類圖
-
使用
把對象變成觀察者 , 實現(xiàn)Observer接口, 使用addObserver()或者deleteObserver()進(jìn)行 訂閱/刪除主題
-
可觀察者(主題)送出通知
-
調(diào)用setChanged()方法, 標(biāo)記數(shù)據(jù)已改變
setChanged(){ changed = true; } notifyObservers(Object arg){ if(changed){ //只有當(dāng)changed為true時才進(jìn)行通知 for every observer on the list{ call update(this,arg) } changed = false; //通知觀察者后,將changed設(shè)置為false } } notifyObservers(){ notifyObservers(null); }
-
調(diào)用notifyObservers() 通知所有觀察者
- notifyObservers();
- notifyObservers(Object args);
-
-
觀察者接收通知
- update(Observable o , Object arg) //拿到可觀察者引用,即可拉取數(shù)據(jù)
public void update(Observable obs, Object arg) { if (obs instanceof WeatherData) { WeatherData weatherData = (WeatherData)obs; this.temperature = weatherData.getTemperature(); this.humidity = weatherData.getHumidity(); display(); } }
參考
? 書籍: HeadFirst設(shè)計模式
? 代碼參考地址: 我就是那個地址