1.定義
觀察者模式說白了就是事件;之前沒有觀察者模式的時候,當我們需要考察屬性的狀態然后作出相應的處理,我們只能當狀態滿足的時候去調用方法。
不過這樣就造成了事件處理和事件綁定在了一起,一旦我們要添加其他的處理功能,就不得不修改原有的檢測代碼,對於修改並不友好。
2.觀察者模式的發展
2.1 一個簡單的需求
對於一個簡單的流程比如說,檢測誰有沒有燒開,以前沒有事件的時候,如果我們就需要寫如下代碼;
public class BoilWater { public int waterTemperature = 0; public void Boil() { for (int i = 0; i < 100; i++) { waterTemperature = i; Thread.Sleep(100); if (waterTemperature == 100) { Call(); } } } public void Call() { Console.Write("The Water is Boiled!"); } }
如上就是檢測看,誰有沒有燒開,如果水燒開了(到達100C),就調用通知方法(Call);
但是上面的代碼缺點也很明顯,那就是:一旦我們要增加水燒開后的處理程序,我們就不得不修改Boil方法;
那有沒有辦法可以讓我們不修改代碼的情況下,在運行中動態的去修改水燒開時的處理程序呢?答案是肯定的,那就是使用觀察者模式;
2.2 觀察者模式
public class BoilWater { public int waterTemperature = 0; public IList<RunInter> Observers = new List<RunInter>(); public void Boil() { for (int i = 0; i < 100; i++) { waterTemperature = i; Thread.Sleep(100); if (waterTemperature == 100) { CallAllObserve(); } } } public void CallAllObserve() { foreach (var item in Observers) { item.Run(); } } public void AddObserver(RunInter runner) { Observers.Add(runner); } public void RemoveObserver(RunInter runner) { Observers.Remove(runner); } } public interface RunInter { void Run(); } public class Call : RunInter { public void Run() { Console.Write("The Water is Boiled!"); } } public class DoOtherThing : RunInter { public void Run() { Console.Write("Do Other Things"); } }
這里我們在被觀察的類中放一個存儲接口的列表,然后在事件滿足的時候,將列表中的每一個接口都執行一遍,而且因為有AddObserver和RemoveObserver方法,因此在程序運行的過程中我們也可以動態的添加新的處理代碼,或刪除已有的處理代碼。
但是這樣做確實是有一點繁瑣,要定義接口,還要再檢測類中加上對應的列表,還好.NET已經為我們設計了完美的替代方案,那就是使用委托和事件;
2.3 使用委托和事件來實現觀察者模式
public class BoilWater { public int waterTemperature = 0; private event Action actions; public void Boil() { for (int i = 0; i < 100; i++) { waterTemperature = i; Thread.Sleep(100); if (waterTemperature == 100) { actions(); } } } public void AddObserver(Action runner) { actions += runner; } public void RemoveObserver(Action runner) { actions -= runner; } }
調用的時候只需要:
var boilWater = new BoilWater(); boilWater.AddObserver(() => { Console.WriteLine("Water is Boiled"); }); boilWater.AddObserver(() => { Console.WriteLine("Do Other Things"); });
這樣當水開的時候,兩個控制台打印的方法,都會被執行了。
其實在這里的Action就是.Net自帶的委托對象,其實委托我們在和上面的代碼對比后,就可以認為他是一系列相似方法的集合列表,當執行委托之后,這個集合中的每一個方法就會依次被執行(有先后順序,並不是並發執行);
但這種方式就為我們實現觀察者模式的邏輯提供的很大的方便。
2.4 委托和事件的區別
2.3中的代碼我使用了event關鍵字,就是講委托聲明為事件了,很多人區分不出委托和事件的區別,其實很簡單,事件是一種特殊的委托;
既然是委托,其實說到底他還是方法的集合,當執行事件后,這個集合中的所有方法都會被執行;
區別在於,委托可以在委托所屬的類外面進行添加和刪除操作;但是事件不行,事件只能在事件所屬的類內部進行添加和刪除操作。
3.特點
優點:將事件的發生和事件的處理分離開,並且可以動態的添加和刪除處理方法
缺點:處理方法中如果有互相引用的情況,就會引起系統崩潰,而且解耦的手法影響了代碼的執行效率(所有的設計模式幾乎都有該缺點)
希望各位學習愉快;