1. 程式人生 > >C#設計模式---觀察者模式簡單例子

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