• 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#观察者模式简单例子下载:

    点击打开链接






  • 相关阅读:
    【bzoj1499】[NOI2005]瑰丽华尔兹 【单调队列优化dp】
    【poj3709】K-Anonymous Sequence 【斜率优化dp】
    【bzoj4566】[Haoi2016]找相同字符【后缀自动机】
    【Play】蜂鸣器音乐 校歌
    【poj2373】Dividing the Path【单调队列优化dp】
    【bzoj2010】SubString【后缀自动机+LCT】
    【bzoj1047】[HA蛤OI2007]理想的正方形【单调队列】
    【poj1743】Musical Theme【后缀数组】
    【bzoj4293】[PA2015]Siano【线段树】
    【bzoj4553】[Tjoi2016&Heoi2016]序列【树套树 树状数组套平衡树】
  • 原文地址:https://www.cnblogs.com/jchubby/p/4429725.html
Copyright © 2020-2023  润新知