天天看點

C#設計模式---觀察者模式簡單例子

版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/qq1010885678/article/details/38169159

在開發過程中經常遇到一個子產品中的 一個方法調用了其他子產品中相關的方法

比如說在一個系統中,如果出現了錯誤,就調用專門進行錯誤處理的子產品中的方法進行錯誤處理

而因為錯誤處理的操作有很多,是以将這些具體的操作封裝在其他的子產品中

在專門進行錯誤處理的子產品中調用其他子產品中的錯誤操作方法

這樣一來在主系統中隻要執行個體化專門進行錯誤處理的子產品對象

并調用其相關的方法,其他子產品中的具體方法也都會被執行

這時專門進行錯誤處理的子產品被稱為釋出者

其他擁有具體錯誤操作的子產品稱為訂閱者

隻要釋出者一釋出資訊(方法被調用)

所有的訂閱者都會相應(具體方法也會執行)

最直接的做法是在子產品中引用并執行個體化其他子產品的對象

然後調用其方法

下面給出一個簡單的例子,用類來模拟子產品

首先是具體的錯誤操作

在例子中有三個具體的錯誤操作

分别為:發送郵件,發出警報,視窗抖動

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