摘要:當氣象局發布新的天氣數據后,兩個公告牌上顯示的天氣數據必須實時更新。氣象局同時要求我們保證程序擁有足夠的可擴展性,因為后期隨時可能要新增新的公告牌。
場景轉載請注明出處:https://zhuanlan.zhihu.com/p/20540213
文章中的例子和思路均來自于《Head First》
我們接到一個來自氣象局的需求:氣象局需要我們構建一套系統,這系統有兩個公告牌,分別用于顯示當前的實時天氣和未來幾天的天氣預報。當氣象局發布新的天氣數據(WeatherData)后,兩個公告牌上顯示的天氣數據必須實時更新。氣象局同時要求我們保證程序擁有足夠的可擴展性,因為后期隨時可能要新增新的公告牌。
概況這套系統中主要包括三個部分:氣象站(獲取天氣數據的物理設備)、WeatherData(追蹤來自氣象站的數據,并更新公告牌)、公告牌(用于展示天氣數據)
WeatherData知道如何跟氣象站聯系,以獲得天氣數據。當天氣數據有更新時,WeatherData會更新兩個公告牌用于展示新的天氣數據。
錯誤示范我們現來看看隔壁老王的實現思路:
public class WeatherData { //實例變量聲明 ... public void measurementsChanged() { float temperature = getTemperature(); float humidity = getHumidity(); float pressure = getPressure(); ListforecastTemperatures = getForecastTemperatures(); //更新公告牌 currentConditionsDisplay.update(temperature, humidity, pressure); forecastDisplay.update(forecastTemperatures); } ... }
上面這段代碼是典型的針對實現編程,這會導致我們以后增加或刪除公告牌時必須修改程序。我們現在來看看觀察者模式,然后再回來看看如何將觀察者模式應用到這個程序。
觀察者模式介紹觀察者模式面向的需求是:A對象(觀察者)對B對象(被觀察者)的某種變化高度敏感,需要在B變化的一瞬間做出反應。舉個例子,新聞里喜聞樂見的警察抓小偷,警察需要在小偷伸手作案的時候實施抓捕。在這個例子里,警察是觀察者、小偷是被觀察者,警察需要時刻盯著小偷的一舉一動,才能保證不會錯過任何瞬間。程序里的觀察者和這種真正的【觀察】略有不同,觀察者不需要時刻盯著被觀察者(例如A不需要每隔1ms就檢查一次B的狀態),二是采用__注冊__(_Register_)或者成為__訂閱__(_Subscribe_)的方式告訴被觀察者:我需要你的某某狀態,你要在它變化時通知我。采取這樣被動的觀察方式,既省去了反復檢索狀態的資源消耗,也能夠得到最高的反饋速度。
觀察者模式通常基于__Subject__和__Observer__接口類來設計,下面是是類圖:
觀察者模式的應用結合上面的類圖,我們現在將觀察者模式應用到WeatherData項目中來。于是有了下面這張類圖:
主題接口
/** * 主題(發布者、被觀察者) */ public interface Subject { /** * 注冊觀察者 */ void registerObserver(Observer observer); /** * 移除觀察者 */ void removeObserver(Observer observer); /** * 通知觀察者 */ void notifyObservers(); }
觀察者接口
/** * 觀察者 */ public interface Observer { void update(); }
公告牌用于顯示的公共接口
public interface DisplayElement { void display(); }
下面我們再來看看WeatherData是如何實現的
public class WeatherData implements Subject { private Listobservers; private float temperature;//溫度 private float humidity;//濕度 private float pressure;//氣壓 private List forecastTemperatures;//未來幾天的溫度 public WeatherData() { this.observers = new ArrayList (); } @Override public void registerObserver(Observer observer) { this.observers.add(observer); } @Override public void removeObserver(Observer observer) { this.observers.remove(observer); } @Override public void notifyObservers() { for (Observer observer : observers) { observer.update(); } } public void measurementsChanged() { notifyObservers(); } public void setMeasurements(float temperature, float humidity, float pressure, List forecastTemperatures) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; this.forecastTemperatures = forecastTemperatures; measurementsChanged(); } public float getTemperature() { return temperature; } public float getHumidity() { return humidity; } public float getPressure() { return pressure; } public List getForecastTemperatures() { return forecastTemperatures; } }
顯示當前天氣的公告牌CurrentConditionsDisplay
public class CurrentConditionsDisplay implements Observer, DisplayElement { private WeatherData weatherData; private float temperature;//溫度 private float humidity;//濕度 private float pressure;//氣壓 public CurrentConditionsDisplay(WeatherData weatherData) { this.weatherData = weatherData; this.weatherData.registerObserver(this); } @Override public void display() { System.out.println("當前溫度為:" + this.temperature + "℃"); System.out.println("當前濕度為:" + this.humidity); System.out.println("當前氣壓為:" + this.pressure); } @Override public void update() { this.temperature = this.weatherData.getTemperature(); this.humidity = this.weatherData.getHumidity(); this.pressure = this.weatherData.getPressure(); display(); } }
顯示未來幾天天氣的公告牌ForecastDisplay
public class ForecastDisplay implements Observer, DisplayElement { private WeatherData weatherData; private ListforecastTemperatures;//未來幾天的溫度 public ForecastDisplay(WeatherData weatherData) { this.weatherData = weatherData; this.weatherData.registerObserver(this); } @Override public void display() { System.out.println("未來幾天的氣溫"); int count = forecastTemperatures.size(); for (int i = 0; i < count; i++) { System.out.println("第" + i + "天:" + forecastTemperatures.get(i) + "℃"); } } @Override public void update() { this.forecastTemperatures = this.weatherData.getForecastTemperatures(); display(); } }
到這里,我們整個氣象局的WeatherData應用就改造完成了。兩個公告牌CurrentConditionsDisplay和ForecastDisplay實現了Observer和DisplayElement接口,在他們的構造方法中會調用WeatherData的registerObserver方法將自己注冊成觀察者,這樣被觀察者WeatherData就會持有觀察者的應用,并將它們保存到一個集合中。當被觀察者`WeatherData狀態發送變化時就會遍歷這個集合,循環調用觀察者公告牌更新數據的方法。后面如果我們需要增加或者刪除公告牌就只需要新增或者刪除實現了Observer和DisplayElement接口的公告牌就好了。
觀察者模式將觀察者和主題(被觀察者)徹底解耦,主題只知道觀察者實現了某一接口(也就是Observer接口)。并不需要觀察者的具體類是誰、做了些什么或者其他任何細節。任何時候我們都可以增加新的觀察者。因為主題唯一依賴的東西是一個實現了Observer接口的對象列表。
好了,我們測試下利用觀察者模式重構后的程序:
public class ObserverPatternTest { public static void main(String[] args) { WeatherData weatherData = new WeatherData(); CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData); ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData); ListforecastTemperatures = new ArrayList (); forecastTemperatures.add(22f); forecastTemperatures.add(-1f); forecastTemperatures.add(9f); forecastTemperatures.add(23f); forecastTemperatures.add(27f); forecastTemperatures.add(30f); forecastTemperatures.add(10f); weatherData.setMeasurements(22f, 0.8f, 1.2f, forecastTemperatures); } }
輸出結果:
當前溫度為:22.0℃ 當前濕度為:0.8 當前氣壓為:1.2 未來幾天的氣溫 第0天:22.0℃ 第1天:-1.0℃ 第2天:9.0℃ 第3天:23.0℃ 第4天:27.0℃ 第5天:30.0℃ 第6天:10.0℃
源碼地址:https://github.com/BaronZ88/DesignPatterns/tree/master/src/com/baron/patterns/observer
如果大家喜歡這一系列的文章,歡迎關注我的知乎專欄和GitHub。
知乎專欄:https://zhuanlan.zhihu.com/baron
GitHub:https://github.com/BaronZ88
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/66517.html
摘要:關鍵概念理解觀察者設計模式中主要區分兩個概念觀察者指觀察者對象,也就是消息的訂閱者被觀察者指要觀察的目標對象,也就是消息的發布者。 原文首發于微信公眾號:jzman-blog,歡迎關注交流! 最近補一下設計模式相關的知識,關于觀察者設計模式主要從以下幾個方面來學習,具體如下: 什么是觀察者設計模式 關鍵概念理解 通知觀察者的方式 觀察者模式的實現 觀察者模式的優缺點 使用場景 下面...
摘要:觀察者模式與發布訂閱的區別在模式中,知道,同時還保留了的記錄。發布者訂閱者在大多情況下是異步方式使用消息隊列。圖片源于網絡侵權必刪如果以結構來分辨模式,發布訂閱模式相比觀察者模式多了一個中間件訂閱器,所以發布訂閱模式是不同于觀察者模式的。 學習了一段時間設計模式,當學到觀察者模式和發布訂閱模式的時候遇到了很大的問題,這兩個模式有點類似,有點傻傻分不清楚,博客起因如此,開始對觀察者和發布...
摘要:總結一下從表面上看觀察者模式里,只有兩個角色觀察者被觀察者而發布訂閱模式,卻不僅僅只有發布者和訂閱者兩個角色,還有第三個角色經紀人存在。參考鏈接觀察者模式發布訂閱模式 做了這么長時間的 菜鳥程序員 ,我好像還沒有寫過一篇關于設計模式的博客...咳咳...意外,純屬意外。所以,我決定,從這一刻起,我要把設計模式在從頭學習一遍,不然都對不起我這 菜鳥 的身份。那這次,就從觀察者模式開始好啦...
摘要:總結一下從表面上看觀察者模式里,只有兩個角色觀察者被觀察者而發布訂閱模式,卻不僅僅只有發布者和訂閱者兩個角色,還有第三個角色經紀人存在。參考鏈接觀察者模式發布訂閱模式 做了這么長時間的 菜鳥程序員 ,我好像還沒有寫過一篇關于設計模式的博客...咳咳...意外,純屬意外。所以,我決定,從這一刻起,我要把設計模式在從頭學習一遍,不然都對不起我這 菜鳥 的身份。那這次,就從觀察者模式開始好啦...
閱讀 2102·2023-05-11 16:55
閱讀 3503·2021-08-10 09:43
閱讀 2617·2019-08-30 15:44
閱讀 2440·2019-08-29 16:39
閱讀 583·2019-08-29 13:46
閱讀 2005·2019-08-29 13:29
閱讀 921·2019-08-29 13:05
閱讀 691·2019-08-26 13:51