標籤:設計模式 c#
在開發過程中經常遇到一個模組中的 一個方法調用了其他模組中相關的方法
比如說在一個系統中,如果出現了錯誤,就調用專門進行錯誤處理的模組中的方法進行錯誤處理
而因為錯誤處理的操作有很多,所以將這些具體的操作封裝在其他的模組中
在專門進行錯誤處理的模組中調用其他模組中的錯誤操作方法
這樣一來在主系統中只要執行個體化專門進行錯誤處理的模組對象
並調用其相關的方法,其他模組中的具體方法也都會被執行
這時專門進行錯誤處理的模組被稱為發行者
其他擁有具體錯誤操作的模組稱為訂閱者
只要發行者一發布資訊(方法被調用)
所有的訂閱者都會相應(具體方法也會執行)
最直接的做法是在模組中引用並執行個體化其他模組的對象
然後調用其方法
下面給出一個簡單的例子,用類來類比模組
首先是具體的錯誤操作
在例子中有三個具體的錯誤操作
分別為:發送郵件,發出警報,視窗抖動
/// <summary> /// 具體的錯誤處理方式類(模組)1,此為訂閱者 /// </summary> public class Handle1 { /// <summary> /// 出現錯誤時做出發送郵件的處理 /// </summary> public void ErrorHanding() { Console.WriteLine("出現錯誤!發送了一封郵件到管理員!"); } }
/// <summary> /// 具體的錯誤處理方式類(模組)2,此為訂閱者 /// </summary> public class Handle2 { /// <summary> /// 出現錯誤時做出發出警報的處理 /// </summary> public void ErrorHanding() { Console.WriteLine("出現錯誤!警報!!!!!!!!!!!!!!!!"); } }
/// <summary> /// 具體的錯誤處理方式類(模組)3,此為訂閱者 /// </summary> public class Handle3 { /// <summary> /// 出現錯誤時做出視窗抖動的處理 /// </summary> public void ErrorHanding() { Console.WriteLine("出現錯誤!我抖!!!!!!!!!!!!!!!!!!!!!!!!"); } }
專門進行錯誤處理的模組
/// <summary> /// 系統錯誤處理相關的類(模組),此為資訊的發行者 /// </summary> public class ErrorAbout { /// <summary> /// 錯誤處理的方法,主模組中通過調用此方法來觸發一系列的錯誤處理 /// </summary> public void ErrorHanding() { //執行個體化每一個訂閱者,並調用其方法 Handle1 handle1 = new Handle1(); handle1.ErrorHanding(); Handle2 handle2 = new Handle2(); handle2.ErrorHanding(); Handle3 handle3 = new Handle3(); handle3.ErrorHanding(); } }
主系統中:
class Program { static void Main(string[] args) { //假設在這個位置,系統出現了錯誤,需要進行錯誤處理 Console.WriteLine("系統出現了嚴重錯誤,需要進行一些處理~~~"); //執行個體化錯誤處理相關的類(發行者) ErrorAbout errorAbout = new ErrorAbout(); //只要發行者的方法一執行,所有訂閱者的方法也都會被執行 errorAbout.ErrorHanding(); Console.ReadKey(); } }
運行結果如下:
這麼做完全可以實現需要的功能
但是有何不妥呢?
在主模組中發現錯誤的時候new一個錯誤模組的執行個體,然後調用處理錯誤的方法
在錯誤模組中直接new各個子模組的執行個體,然後在處理錯誤的方法中依次執行
這樣一來,錯誤模組和子模組之間就直接耦合在一起
這是面向過程的處理方式
在子模組方法發生改變的時候,或者錯誤模組需要添加新的處理錯誤的方法時
要對已經開發完畢的錯誤模組進行修改,違反了開放封閉原則
所以要對錯誤模組和子模組進行解耦(物件導向思想)
這時候就用到了觀察者(Observer)模式,又稱為發布-訂閱模式等
實現的方法有兩種
方法一:使用委託
方法二:使用介面
兩種方法都實現了對錯誤模組和子模組的隔離
對兩個模組的操作都是在主模組中完成的
Demo1:使用委託實現
具體錯誤處理方法的委託
/// <summary> /// 具體錯誤處理方法的委託 /// </summary> public delegate void ErrorHandle();
/// <summary> /// 系統錯誤處理相關的類(模組),此為資訊的發行者 /// </summary> public class ErrorAbout { //定義一個 具體錯誤處理方法委託 的變數 private ErrorHandle errorHandle; //向外界提供一個可以向內部委託變數添加的方法 public void AddErrorHanding(ErrorHandle errorHandle) { //將傳進來的方法加入委託變數中 if (this.errorHandle == null) { this.errorHandle = new ErrorHandle(errorHandle); } else { this.errorHandle += new ErrorHandle(errorHandle); } } /// <summary> /// 錯誤處理的方法,主模組中通過調用此方法來觸發一系列的錯誤處理 /// </summary> public void ErrorHanding() { //調用委託,相當於調用了委託中的所有方法 errorHandle(); } }
在使用委託時要注意
不能直接讓外界操作內部的委託
一定要封裝一個方法提供外界以一個安全的方式來操作內部的委託(為什麼說這是一個安全的方式呢?因為在這個方法裡面只能給委託添加方法,不能進行其他的任何操作)
如果直接將委託變數暴露給外界
那麼外界就可以調用委託變數的所有方法
有可能會造成將原本的方法刪除或者覆蓋等情況
(這就是為什麼會有事件這個東西存在的原因)
這也是一種物件導向的思想
在主模組中
class Program { static void Main(string[] args) { //假設在這個位置,系統出現了錯誤,需要進行錯誤處理 Console.WriteLine("系統出現了嚴重錯誤,需要進行一些處理~~~"); //執行個體化錯誤處理相關的類(發行者) ErrorAbout errorAbout = new ErrorAbout(); //向發行者添加訂閱者 Handle1 handle1 = new Handle1(); errorAbout.AddErrorHanding(handle1.ErrorHanding); Handle2 handle2 = new Handle2(); errorAbout.AddErrorHanding(handle1.ErrorHanding); Handle3 handle3 = new Handle3(); errorAbout.AddErrorHanding(handle1.ErrorHanding); //只要發行者的方法一執行,所有訂閱者的方法也都會被執行 errorAbout.ErrorHanding(); Console.ReadKey(); } }
這樣一來就實現了對錯誤模組和其他模組的解耦
任何錯誤具體的操作模組發生了變化
只要在其使用者--主模組中修改即可
Demo2:使用介面實現
首先需要提供一個統一的介面給具體的錯誤處理方式類(模組)
在發行者中可以通過這個介面調用實現了這個介面的所有訂閱者
具體的錯誤處理方式類(模組)需要實現的介面
/// <summary> /// 具體的錯誤處理方式類(模組)需要實現的介面,在發行者中通過此介面可以統一調用訂閱者的方法 /// </summary> public interface IHandle { void ErrorHanding(); }
具體的錯誤處理方式類(模組)
/// <summary> /// 具體的錯誤處理方式類(模組)1,此為訂閱者 /// </summary> public class Handle1:IHandle { /// <summary> /// 出現錯誤時做出發送郵件的處理 /// </summary> public void ErrorHanding() { Console.WriteLine("出現錯誤!發送了一封郵件到管理員!"); } }
/// <summary> /// 具體的錯誤處理方式類(模組)2,此為訂閱者 /// </summary> public class Handle2:IHandle { /// <summary> /// 出現錯誤時做出發出警報的處理 /// </summary> public void ErrorHanding() { Console.WriteLine("出現錯誤!警報!!!!!!!!!!!!!!!!"); } }
/// <summary> /// 出現錯誤時做出視窗抖動的處理 /// </summary> public void ErrorHanding() { Console.WriteLine("出現錯誤!我抖!!!!!!!!!!!!!!!!!!!!!!!!"); }
發行者
/// <summary> /// 系統錯誤處理相關的類(模組),此為資訊的發行者 /// </summary> public class ErrorAbout { /// <summary> /// 訂閱者介面的集合 /// </summary> private List<IHandle> handles = new List<IHandle>(); /// <summary> /// 在主模組中可以通過此方法向 訂閱者介面的集合 中添加新的訂閱者(具體處理錯誤的方法) /// </summary> /// <param name="handle"></param> public void AddErrorHanding(IHandle handle) { handles.Add(handle); } /// <summary> /// 錯誤處理的方法,主模組中通過調用此方法來觸發一系列的錯誤處理 /// </summary> public void ErrorHanding() { //遍曆訂閱者介面的集合 foreach (var handle in handles) { //執行集合中的每個錯誤處理的方法 handle.ErrorHanding(); } } }
主模組中
class Program { static void Main(string[] args) { //假設在這個位置,系統出現了錯誤,需要進行錯誤處理 Console.WriteLine("系統出現了嚴重錯誤,需要進行一些處理~~~"); //執行個體化錯誤處理相關的類(發行者) ErrorAbout errorAbout = new ErrorAbout(); //向發行者添加訂閱者 errorAbout.AddErrorHanding(new Handle1()); errorAbout.AddErrorHanding(new Handle2()); errorAbout.AddErrorHanding(new Handle3()); //只要發行者的方法一執行,所有訂閱者的方法也都會被執行 errorAbout.ErrorHanding(); Console.ReadKey(); } }
委託實現C#觀察者模式簡單例子下載:
點擊開啟連結
介面實現C#觀察者模式簡單例子下載:
點擊開啟連結