zoukankan      html  css  js  c++  java
  • 关于观察着设计模式的四种实现方式

    观察着设计模式

    对你没看错,这里有将近四中方式来事件我们的观察者模式,还有另外一种不太正规的实现方式;

    经典案例:猫叫了,惊动老鼠 和 主人;

    第一种利用面向对象的实现方式,分别使用接口和对接口的实现,也就是多肽的特性来进行拓展;

      public interface Observer
        {
            void Response();  //观察者的响应,如是老鼠见到猫的反映 
        }
    
        public interface Subject
        {
            void Aimed(Observer obs); //针对哪些观察者
        }
    
    
        public class Mouse : Observer
        {
            private string name;
            public Mouse(string name, Subject sub)
            {
                this.name = name;
                sub.Aimed(this);
            }
            public void Response()
            {
                Console.WriteLine(name+"attemp to escaped");
            }
        }
    
        public class Master : Observer
        {
            public Master(Subject sub)
            {
                sub.Aimed(this);
            }
    
            public void Response()
            {
                Console.WriteLine("host waked");
            }
        }
    
        public class Cat : Subject
        {
            private ArrayList objservers;
    
            public Cat()
            {
                this.objservers = new ArrayList();
            }
            public void Aimed(Observer ob)
            {
                this.objservers.Add(ob);
            }
    
            public void Cry()
            {
                Console.WriteLine("Cat Cryed");
    
                foreach (Observer o in this.objservers)
                {
                    o.Response();  //接口的好处 ,面向我们的接口编程,具体的实现,交给我们的实际的实现类的方法滴哎呦;
                }
    
            }
    
        }
    
        public class Test
        {
            static void fuck()
            {
                Cat cat = new Cat();
                Mouse m = new Mouse("nouse1",cat);
                Mouse m2 = new Mouse("nouse2", cat);
                Master ma = new Master(cat);
                cat.Cry();
    
    
            }
        }

    这个示例过于简单了一点,没有参数的出传递,也没有模拟临界条件去触发事件;

    传递消息;

    然后不同的对象做不同的反应; 这个反应我们可以抽象相同的方法名,那就是我们的Action 或 Response,效果还是挺好的;

    这样的效果挺好的;

    总之是感觉非常秒的代码滴呀;效果是非常好滴呀;

    经过一段时间额学习之后,我又对代码尽心了相应的优化;

    namespace ConsoleApplication92
    {
    
        /// <summary>
        /// 
        /// </summary>
        public interface Observer
        {
            /// <summary>
            /// 观察着应该做出的反应;
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="args"></param>
            void Response(object sender, EventArgs args);
    
            /// <summary>
            /// 去订阅某个订阅对象;完善的做法,还有一些,取消订阅的方法和异常处理;
            /// </summary>
            void Subscribe(IList<Observer> observer);
        }
    
    
        /// <summary>
        /// 主题,出发出一个事件;
        /// </summary>
        public interface Subject
        {
            /// <summary>
            /// 敢兴趣的观察着们;
            /// </summary>
            IList<Observer> observers { get; set; }
            /// <summary>
            /// 某个事件
            /// </summary>
            void SomeEvent(int sound);
        }
    
    
    
        public class CatEventArgs : EventArgs
        {
            /// <summary>
            /// 分贝
            /// </summary>
            public int SoundDecibel { get; private set; }
    
            /// <summary>
            /// 参数
            /// </summary>
            private CatEventArgs() { }
    
            /// <summary>
            /// 猫的参数
            /// </summary>
            /// <param name="soundDecibel"></param>
            public CatEventArgs(int soundDecibel)
            {
                this.SoundDecibel = soundDecibel;
            }
    
        }
    
    
        /// <summary>
        /// 
        /// </summary>
        public class Cat : Subject
        {
            public string Name { get; private set; }
    
            /// <summary>
            /// 初始化对象;
            /// </summary>
            /// <param name="name"></param>
            public Cat(string name)
            {
                this.Name = name;
            }
            /// <summary>
            /// 敢兴趣的观察着;
            /// </summary>
            public IList<Observer> observers
            {
                get; set;
            }
    
            public void Sleep()
            {
                Console.WriteLine(this.Name + " sleep.....");
            }
    
            /// <summary>
            /// 某个事件的发生;
            /// 事件的发生我们已经定义好了;
            /// </summary>
            public void SomeEvent(int sound)
            {
    
                //某个事情的发生
                if (sound < 90)
                {
                    Sleep();
                }
                else
                {
                    CatEventArgs agrs = new CatEventArgs(sound);
                    Notify(agrs);
                }
    
    
            }
    
            /// <summary>
            /// 通知我们的额事件订阅者;
            /// </summary>
            /// <param name="args"></param>
            private void Notify(EventArgs args)
            {
                foreach (Observer o in this.observers)
                {
                    o.Response(this, args);
                }
            }
        }
    
    
        public class Mouse : Observer
        {
    
            public string Name { get; private set; }
    
            /// <summary>
            /// 初始化对象;
            /// </summary>
            /// <param name="name"></param>
            public Mouse(string name)
            {
                Name = name;
            }
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="args"></param>
            public void Response(object sender, EventArgs args)
            {
                var c = (Cat)sender;
                var arg = (CatEventArgs)args;
    
                Console.WriteLine(arg.SoundDecibel + "soud deciebel");  //声音额分贝
    
                Console.WriteLine(c.Name + " weak up...");  //这里能够获得事件放生的对象和 相关参数;
    
                Console.WriteLine(this.Name + "mouse run away...");
    
            }
    
            /// <summary>
            /// 去订阅我们的额某个subject
            /// </summary>
            public void Subscribe(IList<Observer> observer)
            {
                observer.Add(this); //把字节给添加进去;
            }
        }
    
    
        public class Client
        {
    
            /// <summary>
            /// 
            /// </summary>
            public static void Test()
            {
    
                ///初始化我们的事件源对象;
                Cat c = new Cat("Tom");
                c.observers = new List<Observer>();
    
    
                Mouse m = new Mouse("jerry");
                m.Subscribe(c.observers); //去订阅某个对象;
    
    
                c.SomeEvent(95);
                Console.ReadLine();
            }
    
        }
    
    
    
    
    
    }

    对于这种模式的抽象,net中已经给我们提供了较好的接口封装;

    那就是我们的IObservable 和 IObserver

    他们两分别等于我们的subject 和我们的observer

    效果是非常好的;

    满分;

     然后,就以此打开我们基于事件编程的大门;

    什么reactive 啊 消息队列啊,rabbitq啊 akka 等等的大门就开了; 这些东西,完全太吸引人了,完全就是一个新大陆滴呀;

    简直就发现了新大陆;

    完美

    这里就是我们的代码示例:

        //先封装我们的参数;  应为是我们的: Iobserver 模式,这里就不用继承自我们的: EventArgs
        public class CatEventArgs
        {
    
            /// <summary>
            /// 声音的分贝
            /// </summary>
            public double SoundQuantity { get; internal set; }
    
            private CatEventArgs() { }
    
            public CatEventArgs(double soundQuantity)
            {
                SoundQuantity = soundQuantity;
            }
        }
    
        /// <summary>
        /// 销毁对象;也就是等于取消订阅;
        /// </summary>
        public class DisposedAction : IDisposable
        {
            private readonly Action _action;
    
            public DisposedAction(Action action)
            {
                _action = action;
            }
    
            public void Dispose()
            {
                _action();  //自动的销毁我们想要的基本对象;
            }
        }
    
        //这只猫需要对外公布两个方法,订阅和取消订阅 以及我们事件的出发点;
        public class Cat : IObservable<CatEventArgs>
        {
            private readonly List<IObserver<CatEventArgs>> _observers;
    
            public double CatSoundQuntity { get; internal set; }
    
            public Cat(double catSoundQuntity=0)
            {
                CatSoundQuntity = catSoundQuntity;
                _observers = new List<IObserver<CatEventArgs>>();  //容器的初始化;
            }
    
            //这里注册,添加,对应的订阅者;
            public IDisposable Subscribe(IObserver<CatEventArgs> observer)
            {
                if (!_observers.Contains(observer))
                {
                    _observers.Add(observer);
                }
                //这里还要去实现一个 IDisposable
                return new DisposedAction(() => _observers.Remove(observer)); //这里实现我们一个销毁对象的方法;
                //返回了取消对象的委托;窝草,这个方法好高级;
            }
            //触发实现的代码,不一定要封装在这里名,触发的事件也可以在外部调经引起的的;
            //这个看我们额事件清楚;
            public void OnTrigger(CatEventArgs e)
            {
                if (_observers.Any())
                {
                    _observers.ForEach(obj => obj.OnNext(e));
                }
            }
    
            public void OnCry()
            {
                //事件触发点;
                for (var i = 0; i < 100; i++)   //模拟声音的
                {
                    if (i >= 60) //大约六十分贝触发事件;
                    {
                        CatEventArgs e = new CatEventArgs(i);
                        OnTrigger(e);//通知订阅者;
                        return; //退出循环,避免连续触发事件;
                    }
                }
            }
        }
    
        //用泛型,这里当然是为了避免我们的参数的装箱和拆箱了;
        public class Mouse : IObserver<CatEventArgs>
        {
            public string Name { get; private set; }
            private IDisposable _Subscriber;
    
            public Mouse(string name)
            {
                Name = name;
            }
            //当然还有我么额取消订阅;
    
            public void Subscribe(Cat c)
            {
                _Subscriber = c.Subscribe(this);  //订阅我们的感兴趣的时间
            }
    
            public void UnSubscribe()
            {
                _Subscriber.Dispose(); //这是一个好的方法;
            }
    
            //对应接口的实现,就在我们这里的拉滴呀;
            public void OnNext(CatEventArgs e)
            {
                Console.WriteLine($"猫的分贝大小是:{e.SoundQuantity}/所以惊动了老鼠");
            }
    
            public void OnError(Exception error)
            {
                Console.WriteLine(error.Message);
            }
    
            public void OnCompleted()
            {
                Console.WriteLine("OnCompleted");
            }
    
    
        }
    
    
        class Program
        {
    
            static void Test()
            {
                Cat cat = new Cat(); //完全可以将事件的出发点放在 类的外面;
    
                Mouse m = new Mouse("Jack");
                m.Subscribe(cat);  //订阅我们所感兴趣的事情;
    
                cat.OnCry(); //事件触发,这个完全暴露给外部来触发事件;
    
    
    
            }
            static void Main(string[] args)
            {
    
    
                Test();
    
                Console.ReadLine();
    
            }
        }

    第二种模式是使用我们委托和事件的特性来进行拓展;

    这种方式里面也包含了面向对象的特性滴呀;

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication4
    {
    
    
        //这里是对应的我们的第二种实现的方式;
    
        public delegate void SubEventHanlder();
    
        public abstract class Subject
        {
            public event SubEventHanlder subEvent;
            protected void FireAway()
            {
                if (this.subEvent != null)
                    this.subEvent();
            }
        }
    
    
        public class Cat : Subject
        {
            public void Cry()
            {
                Console.WriteLine("cat cryed");
                this.FireAway();
            }
        }
    
        public abstract class Observer
        {
            public Observer(Subject sub)
            {
                sub.subEvent += new SubEventHanlder(Response);
            }
    
            public abstract void Response();
        }
    
        public class Mouse : Observer
        {
            private string name;
            public Mouse(string name, Subject sub)
                : base(sub)
            {
                this.name = name;
            }
    
            public override void Response()
            {
                Console.WriteLine(name + "attemp to  escape!");
            }
    
        }
    
        public class Master : Observer
        {
            public Master(Subject sub)
                : base(sub)
            {
    
            }
    
            public override void Response()
            {
                Console.WriteLine("host waked");
            }
    
        }
    
    
        public class Test
        {
            public void t()
            {
                Cat c = new Cat();
                Mouse m1 = new Mouse("m1", c);
                Mouse m2 = new Mouse("m2", c);
                Master m = new Master(c);
                c.Cry();
            }
        }
    
    
    }

    后面我们再看看以前,一篇关于烧开水的 观察模式额实例

    那个实例也是比较经典滴呀;效果非常好滴呀;

    好今天,我们就来复习这个精当的烧开的经典示例;

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication6
    {
        //热水器
    
         public class Heater
        {
            private int temperature;
            public delegate void BoilHandler(int param);
            public event BoilHandler BoilEvent;
    
            //烧水;
            public void BoilWater()
            {
                for(var i = 0; i <= 100; i++)
                {
                    temperature = i;
                    if (temperature==95)
                    {
                        if (BoilEvent != null)
                        {
                            BoilEvent(temperature);  //通知所有对这个事件感兴趣的观察着; 其实这里还没考虑到事件的安全性低呀;效果就明显不一样了滴呀;
                        }
                    }
                }
            }
    
        }
    
        // 警报器
        public class Alarm
        {
            public void MakeAlert(int param)
            {
                Console.WriteLine("Alarm:嘀嘀嘀,水已经 {0} 度了:", param);
            }
        }
    
        //显示器
        public class Display
        {
            public static void ShowMsg(int param)
            { //静态方法
                Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", param);
            }
        }
    
        //其他;
        public class OtherObserver
        {
            public static void FuckLife(int param)
            {
                Console.WriteLine("其他,待扩展额程序.....{0}", param);
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
    
                
                Heater heater = new Heater();
    
                Alarm alarm = new Alarm();
    
                //在类的进行事件的注册;
    
                heater.BoilEvent+= alarm.MakeAlert;
                heater.BoilEvent += Display.ShowMsg;
                heater.BoilEvent += OtherObserver.FuckLife;
    
                //
                heater.BoilWater();   //烧水
    
                Console.ReadLine();
    
    
            }
        }
    }

    //然后是我们 net 中事件;

    Net Framework中的委托与事件

    尽管上面的范例很好地完成了我们想要完成的工作,但是我们不仅疑惑:为什么.Net Framework 中的事件模型和上面的不同?为什么有很多的EventArgs参数?

    在回答上面的问题之前,我们先搞懂 .Net Framework的编码规范:

    • 委托类型的名称都应该以EventHandler结束。
    • 委托的原型定义:有一个void返回值,并接受两个输入参数:一个Object 类型,一个 EventArgs类型(或继承自EventArgs)。
    • 事件的命名为 委托去掉 EventHandler之后剩余的部分。
    • 继承自EventArgs的类型应该以EventArgs结尾。

    再做一下说明:

    1. 委托声明原型中的Object类型的参数代表了Subject,也就是监视对象,在本例中是 Heater(热水器)。回调函数(比如Alarm的MakeAlert)可以通过它访问触发事件的对象(Heater)。
    2. EventArgs 对象包含了Observer所感兴趣的数据,在本例中是temperature。

      

      最后的完整版代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication7
    {
    
        //web developer information;
        //这里委托的原型定义:一个void返回值,并接受两个参数一个object类型的,一个eventArgs类型(继承自EventArgs)
    
        public class Heater
        {
            private int temperature;
            public string type = "007";
            public string area = "chengdu";
    
            public delegate void BoildEventHanlder(Object sender, BoiledEventArgs e);
    
            public event BoildEventHanlder Boiled; 
    
            //定义一个boiledEventArg类,传递个感兴趣的observer对象;
            public class BoiledEventArgs : EventArgs
            {
                public readonly int temperature;
    
                public BoiledEventArgs(int temp)
                {
                    this.temperature = temp;
                }
            }
    
    
            //提供一个可重写的,比变继承类可以拒绝其他对象对它的监视
    
            protected virtual void OnBoiled(BoiledEventArgs e)
            {
                if (Boiled != null)
                {
                    Boiled(this, e);  // 调用所有注册对象的方法
                }
            }
    
            // 烧水。
            public void BoilWater()
            {
                for (int i = 0; i <= 100; i++)
                {
                    temperature = i;
                    if (temperature > 95)
                    {
                        //建立BoiledEventArgs 对象。
                        BoiledEventArgs e = new BoiledEventArgs(temperature);
                        OnBoiled(e);  // 调用 OnBolied方法
                    }
                }
            }
        }
    
    
    
        // 警报器
        public class Alarm
        {
            public void MakeAlert(Object sender, Heater.BoiledEventArgs e)
            {
                Heater heater = (Heater)sender;     //这里是不是很熟悉呢?
                                                    //访问 sender 中的公共字段
                Console.WriteLine("Alarm:{0} - {1}: ", heater.area, heater.type);
                Console.WriteLine("Alarm: 嘀嘀嘀,水已经 {0} 度了:", e.temperature);
                Console.WriteLine();
            }
        }
    
    
        // 显示器
        public class Display
        {
            public static void ShowMsg(Object sender, Heater.BoiledEventArgs e)
            {   //静态方法
                Heater heater = (Heater)sender;
                Console.WriteLine("Display:{0} - {1}: ", heater.area, heater.type);
                Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", e.temperature);
                Console.WriteLine();
            }
        }
    
    
    
    
    
    
    
    class Program
        {
            static void Main(string[] args)
            {
    
    
                //警报器和显示器的实现没有太大的变化;  主要是我们的额参数变化比较大
    
                Heater h = new Heater();
                Alarm alarm = new Alarm();
                h.Boiled += alarm.MakeAlert;
                h.Boiled += Display.ShowMsg;
    
                h.BoilWater();
    
                Console.ReadKey();
    
    
            }
        }
    }

    关于事件的使用,我们不得不考虑到内存泄漏的问题滴呀;

    //然后这里我们再补充先关知识;

    http://blog.csdn.net/hulihui/article/details/3217649 

    原文:

    https://www.codeproject.com/Articles/29922/Weak-Events-in-C

    然后,扩展学习,事件中的深入了解;

    http://www.uml.org.cn/net/201303193.asp

      

    常见的net中内存泄漏问题;

  • 相关阅读:
    一些shell脚本实例
    python 创建虚拟环境python –m venv方式
    jmeter 压测的执行步骤步骤
    jmeter dubbo测试
    python 时间戳转日期 不自动补零 without zero-padding
    windows jetbrains toolbox 无法修改应用安装目录(应用正在运行)的解决方案
    【摘】人生苦短, 每日python
    【摘】人生苦短, 每日python
    【摘】人生苦短,每日python
    【摘】人生苦短,每日python
  • 原文地址:https://www.cnblogs.com/mc67/p/7158205.html
Copyright © 2011-2022 走看看