前言:java
這一節開始學習觀察者模式,開始講以前會先像第一節那樣經過一個應用場景來引入該模式。具體場景爲:氣象站提供了一個WeatherData對象,該對象能夠追蹤獲取天氣的溫度、氣壓、溼度信息,WeatherData對象會隨即更新三個佈告板的顯示:目前情況(溫度、溼度、氣壓)、氣象統計和天氣預報。編程
WeatherData類圖以下:學習
說明:測試
GetTemperature()、GetHumidity()、GetPressure()分別用來獲取天氣溫度、溼度氣壓,MeasurementsChanged()當氣象測量更新此方法會被調用。this
分析:spa
實現:設計
public class WeatherData { public void MeasurementsChanged() { float temp = GetTemperature(); float humidity = GetHumidity(); float pressure = GetPressure(); currentConditionDisplay.update(temp, humidity, pressure); statisticsDisplay.update(temp, humidity, pressure); forecastDisplay.update(temp, humidity, pressure); } //其餘方法 }
反思:code
咱們的這種實現有何不妥?server
結合咱們第一節中的一些面向對象的原則,這種實現方式會有以下問題:對象
l 針對具體實現編程,後續新增或者刪除佈告板必須修改程序
l 未將改變的地方封裝起來
這種實現方式與面向對象的一些基本原則是相違背的,目前暫時是實現了用戶的需求,可是在後續不具有可擴展行。
出版者:就至關於「主題」(Subject),訂閱者至關於「觀察者」(Observer)
當出版者(主題)發行新的報紙的時候,全部的觀察者(訂閱者)就能夠收到最新的報紙,同時,當新的觀察者(訂閱者)加入時,也能夠收到最新的報紙,當觀察者(訂閱者)退訂報紙後,就再也收不到新的報紙。
觀察者模式定義了對象之間一對多依賴,這樣一來,當一個對象狀態改變時,它的全部依賴者都會收到通知並自動更新。
主題和觀察者定義一對多的關係。觀察者依賴於此主題,只要主題狀態一有變化,觀察者就會被通知,根據通知的風格,觀察者可能所以新值而更新。
觀察者模式:類圖
鬆耦合的威力
l 當兩個對象之間鬆耦合,它們依然能夠交互,可是不清楚彼此的細節。
l 觀察者模式提供了一種對象設計,讓主題和觀察者之間鬆耦合。
說明:
鬆耦合的設計之因此能讓咱們創建有彈性的OO系統,可以應對變化,是由於對象之間的相互依賴講到了最低。
類圖:
/// Description:對象、觀察者、顯示接口 /// </summary> public interface ISubject { void RegisterObserver(IObserver o);//註冊觀察者 void RemoveObserver(IObserver o);//刪除觀察者 void NotifyObervers();//通知觀察者 } public interface IObserver { void Update(float temp, float humidity, float pressure); } public interface IDisplayElement { void Display(); }
/// Description:WeatherData 註冊、刪除、通知觀察者 /// </summary> public class WeatherData:ISubject { private ArrayList observers; private float temperature; private float humidity; private float pressure; public WeatherData() { observers = new ArrayList();//初始化obervers,用來存儲註冊的觀察者 } /// <summary> /// 註冊觀察者 /// </summary> /// <param name="o"></param> public void RegisterObserver(IObserver o) { observers.Add(o); } /// <summary> /// 刪除觀察者 /// </summary> /// <param name="o"></param> public void RemoveObserver(IObserver o) { int i = observers.IndexOf(o); if (i >= 0) observers.Remove(o); } /// <summary> /// 通知觀察者 /// </summary> public void NotifyObervers() { foreach (IObserver o in observers) { o.Update(temperature, humidity, pressure); } } /// <summary> /// 當從氣象站獲得更新觀測值時,通知觀察者 /// </summary> public void MeasurementsChanged() { NotifyObervers(); } /// <summary> /// /// </summary> /// <param name="temperature"></param> /// <param name="humidity"></param> /// <param name="pressure"></param> public void SetMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; MeasurementsChanged(); } }
/// Description:建立佈告板 /// </summary> public class CurrentConditionsDisplay:IObserver,IDisplayElement { private float temperature; private float humidity; private ISubject weatherData; public CurrentConditionsDisplay(ISubject 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() { Console.WriteLine("Current coditions: " + temperature + "F degress and " + humidity + "% humidity"); } }
WeatherStation.WeatherData weatherData = new WeatherStation.WeatherData(); WeatherStation.CurrentConditionsDisplay currentDisplay = new WeatherStation.CurrentConditionsDisplay(weatherData); weatherData.SetMeasurements(10, 20, 30);
結果以下:
Java內置的觀察者模式,許多功能都已經事先準備好了,甚至能夠用推(push)或拉(pull)的方式傳送數據。
使用java內置觀察者模式實現氣象站的OO設計類圖,以下:
Java內置觀察者模式與咱們在3小節中明顯的差別是WeatherData繼承自Observable類,並集成了一些增長、刪除、通知觀察者的方法。
l 將對象變成觀察者
首先仍是要實現Observer(觀察者)接口,其次調用Observable對象的addObserver()方法便可。
l 可觀察者(主題)送出通知
l 觀察者接收通知
觀察者實現了Observer的接口,方法簽名以下:
update(Observable o,Object arg)
第一個參數爲主題自己,讓觀察者知道是哪一個主題通知它的
第二個參數是傳入notifyObservers()的數據對象
若是想用「推」的方式將數據給觀察者,則能夠把數據當作數據對象的方式傳給notifyObservers(arg)
若是想用「拉」的方式將數據給觀察者,則須要在update()中,經過WeatherData的getTemperature()等方法獲取對應的氣象值。
缺陷:
Java內置的觀察者模式中Observable是一個類,你必須設計一個類去繼承它。若是某類相同時具備Observable類和另外一個超類的行爲,就沒法實現,由於java不支持多繼承。
同時也限制了Observable的複用能力。
同時,Observable API將setChanged()方法保護了起來,除非繼承自Observable類,不然沒法建立Observable實例組合到本身的對象中,也違背了面向對象設計的第二個原則:多用組合,少用繼承。
l OO原則:
封裝變化
多用組合,少用繼承
針對接口編程,不針對實現編程
對交互對象之間的鬆耦合設計而努力(新的OO原則,鬆耦合的設計更有彈性,更能應對變化)
l OO模式:
觀察者模式—在對象之間定義一對多的依賴,這樣一來,當一個對象改變狀態,依賴它的對象都會收到通知,並自動更新。