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#觀察者模式簡單例子下載: