Head First設計模式——觀察者模式
- 2019 年 10 月 22 日
- 筆記
1、氣象監測應用,錯誤示範
有一個氣象站,分別有三個裝置:溫度感應裝置,濕度感應裝置,氣壓感應裝置。WeathData對象跟蹤氣象站數據,WeathData有MeasurmentsChanged()方法,當感應裝置數據變化後就會調用MeasurmentsChanged。目前需求是要三個布告板,分別是氣象數據狀況(CurrentConditionDisply),氣象統計(StaisticsDisply),天氣預報(ForcastDisply)用來顯示各種的數據。按照這個需求我們可以設計出如下方式
public class WeatherData(){ private float Temperature{get;set;} private float Humidity{get;set;} private float Pressure{get;set;} public void MeasurmentsChanged(){ CurrentConditionDisply.Update(Temperature,Humidity,Pressure); StaisticsDisply.Update(Temperature,Humidity,Pressure); ForcastDisply.Update(Temperature,Humidity,Pressure); } } public class CurrentConditionDisply{ public void Update(float temperature,float humidity,float Pressure){ //更新公布數據 } } public class StaisticsDisply{ public void Update(float temperature,float humidity,float Pressure){ //更新統計數據 } } public class ForcastDisply{ public void Update(float temperature,float humidity,float Pressure){ //更新天氣預報 } }
按照如上設計也能實現目前需求,但是如果新加入一種布告板或者刪除一個布告板,那麼我們需要去求改MeasurmentsChanged方法。
此例子帶來的問題:
1、我們是針對實現編程,而非爭對接口。
2、對於每個新的布告板,我們都得修改代碼。
3、無法在運行是動態地增加或者刪除布告板。
4、未封裝改變的部分,違反了對修改關閉,對擴展開放。
2、使用觀察者模式解耦
觀察者模式:定義了對象之間的一對多依賴,當一個對象改變時,他的所有依賴都會收到通知並自動更新。
訂閱報紙就是典型的觀察者模式,出版社即為主題(subject),訂閱者即是觀察者(observer),當有新報紙時,報社就會派人送新報紙到訂閱了該報紙的讀者手上。這裡先給出訂閱模式類圖,然後我們再對之前的氣象站進行觀察者模式封裝。
3、利用觀察者模式改進氣象站
按照觀察者模式進行設計和改進氣象站代碼並測試
/// <summary> /// 主題 /// </summary> public interface Subject { public void RegisterObserver(Observer o); public void RemoveObserver(Observer o); public void NotifyObserver(); } /// <summary> /// 具體主題(氣象站) /// </summary> public class WeatherData : Subject { private List<Observer> observers; private float Temperature { get; set; } private float Humidity { get; set; } private float Pressure { get; set; } public WeatherData() { observers = new List<Observer>(); } public void RegisterObserver(Observer o) { observers.Add(o); } public void RemoveObserver(Observer o) { observers.Remove(o); } //通知觀察者 public void NotifyObserver() { foreach (var o in observers) { o.Update(Temperature, Humidity, Pressure); } } public void MeasurmentsChanged() { NotifyObserver(); } //數據變化 public void SetMeasurments(float temperature, float humidity, float pressure) { Temperature = temperature; Humidity = humidity; Pressure = pressure; MeasurmentsChanged(); } } /// <summary> /// 訂閱者 /// </summary> public interface Observer { void Update(float temperature, float humidity, float pressure); } public class CurrentConditionDisply : Observer { private Subject weatherData; public CurrentConditionDisply(Subject weatherData) { this.weatherData = weatherData; weatherData.RegisterObserver(this); } public void Update(float temperature, float humidity, float pressure) { Console.WriteLine($"當前情況布告板:{temperature},{humidity},{pressure}"); } } public class StaisticsDisply : Observer { private Subject weatherData; public StaisticsDisply(Subject weatherData) { this.weatherData = weatherData; weatherData.RegisterObserver(this); } public void Update(float temperature, float humidity, float pressure) { Console.WriteLine($"統計數據布告板:{temperature},{humidity},{pressure}"); } } public class ForcastDisply : Observer { private Subject weatherData; public ForcastDisply(Subject weatherData) { this.weatherData = weatherData; weatherData.RegisterObserver(this); } public void Update(float temperature, float humidity, float pressure) { Console.WriteLine($"天氣預報布告板:{temperature},{humidity},{pressure}"); } }
測試結果: