关于观察着设计模式的四种实现方式

观察着设计模式

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

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

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

  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中内存泄漏问题;

原文地址:https://www.cnblogs.com/mc67/p/7158205.html