前言: 這篇文章我們以Head First設(shè)計(jì)模式中講解的氣象站為例,通過(guò)它的案列進(jìn)行學(xué)分析和編碼(C#)測(cè)試,并歸納總結(jié)出觀察者模式。
1、氣象監(jiān)測(cè)案列,錯(cuò)誤示范實(shí)現(xiàn)
一個(gè)氣象站,分別有三個(gè)裝置:溫度感應(yīng)裝置,濕度感應(yīng)裝置,氣壓感應(yīng)裝置。WeathData對(duì)象跟蹤氣象站數(shù)據(jù),WeathData有MeasurmentsChanged()方法,當(dāng)感應(yīng)裝置數(shù)據(jù)變化后就會(huì)調(diào)用MeasurmentsChanged對(duì)使用改數(shù)據(jù)的用戶進(jìn)行數(shù)據(jù)更新。目前需求是要三個(gè)布告板,分別是目前氣象數(shù)據(jù)狀況布告板(CurrentConditionDisply)、氣象數(shù)據(jù)統(tǒng)計(jì)布告板(StaisticsDisply)、天氣預(yù)報(bào)布告板(ForcastDisply)。三塊布告板都是需要接收氣象站數(shù)據(jù),然后按需展示到布告板上。針對(duì)這個(gè)需求我們可以如下方式實(shí)現(xiàn):
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){ //更新公布數(shù)據(jù) } } public class StaisticsDisply{ public
void Update(float temperature,float humidity,float Pressure){ //更新統(tǒng)計(jì)數(shù)據(jù) } }
public class ForcastDisply{ public void Update(float temperature,float
humidity,float Pressure){ //更新天氣預(yù)報(bào) } }
?WeatherData是數(shù)據(jù)跟蹤對(duì)象,當(dāng)氣象站數(shù)據(jù)變化時(shí)用MeasurmentsChanged方法來(lái)依次調(diào)用三塊布告板的Update方法更新氣象數(shù)據(jù)。按照這種設(shè)計(jì)能實(shí)現(xiàn)目前需求,但是如果新加入一種布告板或者刪除一個(gè)布告板,那么我們需要去修改MeasurmentsChanged方法新增或者刪除代碼,這就會(huì)造成后期的維護(hù)擴(kuò)展問(wèn)題。這個(gè)例子暴露的問(wèn)題:
1、我們是針對(duì)實(shí)現(xiàn)編程,而非針對(duì)接口。
2、對(duì)于每個(gè)新的布告板,我們都得修改代碼。
3、無(wú)法在運(yùn)行時(shí)動(dòng)態(tài)地增加或者刪除布告板。
4、未封裝改變的部分,違反了對(duì)修改關(guān)閉,對(duì)擴(kuò)展開放原則。
2、使用觀察者模式解耦
由1的實(shí)現(xiàn)和帶來(lái)的問(wèn)題以及它的場(chǎng)景我們可以使用設(shè)計(jì)模式中的觀察者模式很好的滿足這一需求,且后面的維護(hù)擴(kuò)展都很方便。首先我們先了解觀察者模式
觀察者模式:定義了對(duì)象之間的一對(duì)多依賴,當(dāng)一個(gè)對(duì)象改變時(shí),他的所有依賴都會(huì)收到通知并自動(dòng)更新。
訂閱報(bào)紙就是典型的觀察者模式,出版社即為主題(subject),訂閱者即是觀察者(observer),當(dāng)有新報(bào)紙時(shí),報(bào)社就會(huì)派人送新報(bào)紙到訂閱了該報(bào)紙的讀者手上。我們通過(guò)觀察者模式類圖進(jìn)行理解我記憶,然后我們?cè)賹?duì)之前的氣象站進(jìn)行觀察者模式封裝修改。
3、利用觀察者模式改進(jìn)氣象站
按照觀察者模式我們需要定義一個(gè)主題接口Subject,WeatherData作為具體的主題類繼承接口Subject,實(shí)現(xiàn)注冊(cè)移除通知觀察者接口。定義Observer接口,其他三塊布告板繼承Observer實(shí)現(xiàn)自己的更新數(shù)據(jù)方法Update。
/// <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(); }
//數(shù)據(jù)變化 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($"當(dāng)前情況布告板:{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($"統(tǒng)計(jì)數(shù)據(jù)布告板:{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($"天氣預(yù)報(bào)布告板:{temperature},{humidity},{pressure}"); } }
對(duì)使用了觀察者模式的氣象站進(jìn)行測(cè)試,當(dāng)數(shù)據(jù)變化的時(shí)候就會(huì)自動(dòng)通知觀察者并更新數(shù)據(jù),也可以靈活的添加移除觀察者而不用去具體的實(shí)現(xiàn)里面修改代碼。
static void Main(string[] args) { WeatherData weatherData = new
WeatherData(); CurrentConditionDisply currentConditionDisply = new
CurrentConditionDisply(weatherData); StaisticsDisply staisticsDisply = new
StaisticsDisply(weatherData); ForcastDisply forcastDisply = new
ForcastDisply(weatherData); weatherData.SetMeasurments(30, 65, 30.5F);
Console.WriteLine("---------------移除訂閱者-----------");
weatherData.RemoveObserver(currentConditionDisply);
weatherData.SetMeasurments(31,55,20);
Console.WriteLine("---------------添加訂閱者-----------");
weatherData.RegisterObserver(currentConditionDisply);
weatherData.SetMeasurments(30, 55, 30.5F); Console.ReadKey(); }
熱門工具 換一換
