zoukankan      html  css  js  c++  java
  • ABP之事件总线(1)

    什么是事件总线呢?官方的文档说,它是一个单例对象,由其他的类共同拥有,可以用来触发和处理事件。这个东西确实比较陌生,为什么要使用事件总线,或者说事件总线的优势是什么???首先我们可以明确的是,事件总线的出现是用来代替我们传统的事件的,那么我们传统的事件到底有什么不好呢??我们由浅入深,慢慢研究。

    事件我们都比较熟悉,我们用两个例子回顾一下。

    1.第一个例子,以前我们在winform的开发程序中到处都是事件,比如

    sender:触发事件的对象

    e:事件的数据,传递参数

    方法中的内容:事件的处理逻辑。

    2.第二个例子,发布-订阅模式

    发布者:创建委托事件,然后调用委托事件(触发事件)

    订阅者:编写事件的触发逻辑

    以猫捉老鼠的故事为例子

     public class Cat
        {
            /// <summary>
            /// 事件的处理逻辑
            /// </summary>
            /// <param name="mouseName"></param>
            public void CatchMouse(string mouseName)
            {
                Console.WriteLine("小猫说:
    ");
                Console.WriteLine("我抓住了{0}",mouseName);
            }
        }

     老鼠

     public  class Mouse
        {
            //定义委托
            public delegate void CommingHandler(string name);
            //声明事件委托
            public event CommingHandler CommingEventHandler;
            public Mouse(string name)
            {
                this.Name = name;
            }
            public void Comming(string name)
            {
                Console.WriteLine("老鼠说:
    ");
    
                Console.WriteLine("我的名字是{0},I am Comming!", name);
                //发布事件
                CommingEventHandler(name);
            }
            public string Name { get; set; }
            
        }

     猫捉老鼠

     class Program
        {
            static void Main(string[] args)
            {
    
                Cat c1 = new Cat();
                Mouse m1 = new Mouse("1号老鼠");
                //订阅事件
                m1.CommingEventHandler += c1.CatchMouse;
                //触发事件
                m1.Comming(m1.Name);
                Console.ReadLine();
            }
          
        }

     从上面的两个例子中,我们可以总结一点东西,如何组成一个事件??

    第一个例子:sender+e+事件的处理逻辑=事件

    第二个例子:老鼠+老鼠的名字+CatchMouse的处理逻辑=事件

    触发事件的对象+描述事件的参数+事件触发的处理逻辑=事件

    前两个参数都是老鼠的参量统称为事件源,后一个参数是猫的动作统称为事件的处理,所以事件的本质就是事件源和事件的处理逻辑。

    现在又有一个问题来了,如果出现一个需求,我们的猫要订阅一个猫吃一只多少斤鱼的事件FishCommingEventHandler,怎么做呢?此时事件源变成了鱼,事件的逻辑变成了”猫吃鱼“,这个动作,我们通常的做法一般都是重新定义一个委托事件,然后完成相应的处理逻辑。由此可以看出,我们的事件通用性很差劲,每次出现新的需求都需要重新写处理逻辑。变化的点:事件源和事件的处理。

    二. 通用的发布-订阅模式

    (1)抽象事件源

        为了统一事件源的变化,我们定义一个抽象的父类

        /// <summary>
        /// 抽象事件源接口
        /// </summary>
        public  interface IEventData
        {
            /// <summary>
            /// 触发事件的对象
            /// </summary>
           object EventSource { get; set; }
            /// <summary>
            /// 触发事件的时间
            /// </summary>
           DateTime EventTime { get; set; }
        }

        /// <summary>
        /// 所有的事件源都必须继承的父类,事件源就是参数,传递参数的作用,相当于老鼠的名字,鱼的名字
        /// </summary>
        public class EventData : IEventData
        {
            public object EventSource { get ; set ; }
            public DateTime EventTime { get; set; }
            public EventData()
            {
                EventTime = DateTime.Now;
            }
        }
    /// <summary>
        /// 自定义参数,封装变化
        /// </summary>
        public class SelfDefineEventData:EventData
        {
            public string MouseName { get; set; }
            public float FishWeight{ get; set; }
        }

     (2)接下来就是对事件逻辑进行处理,统一事件触发的方法,如果事件的方法不同一,将会造成通用性变差,同时需要手动匹配事件源,所以需要注意的是事件源和事件的处理是绑定在一起的。

     /// <summary>
        /// 标志接口,mark
        /// </summary>
        public interface IEventHandler
        {
          
        }
        /// <summary>
        /// 泛型接口,事件源和事件的处理是一起的
        /// </summary>
        /// <typeparam name="TEventData"></typeparam>
        public interface IEventHandler<TEventData> : IEventHandler where TEventData : IEventData
        {
            /// <summary>
            /// 根据事件源,做出相应的处理
            /// </summary>
            /// <param name="eventData"></param>
            void Handle(TEventData eventData);
        }

    我们为事件1 (猫捉老鼠)和事件2 (猫吃了一只XX的鱼)定义一个统一的委托类

     public delegate void  SelfDefineEventHandler(SelfDefineEventData eventData);

    修改我们老鼠的类和鱼类

      public  class Mouse
        {
            
          
            //声明事件委托
            public event SelfDefineEventHandler selfEventHandler;
            public Mouse(string name)
            {
                this.Name = name;
            }
            public void Comming(string name )
            {
                Console.WriteLine("老鼠说:
    ");
    
                Console.WriteLine("我的名字是{0},I am Comming!", name);
                //触发事件
                selfEventHandler(new SelfDefineEventData() {  MouseName=name});
            }
            public string Name { get; set; }
            
        }
        public class Fish
        {
            //声明事件委托
            public event SelfDefineEventHandler selfEventHandler;
            public Fish(int weight)
            {
                this.Weight = weight;
            }
            public void Comming(int weight)
            {
                Console.WriteLine("小鱼说:
    ");
    
                Console.WriteLine("我的重量是{0},I am Comming!", weight);
                //触发事件
                selfEventHandler(new SelfDefineEventData() { FishWeight = weight });
                
            public int Weight { get; set; }
        }

    从上面的代码中,由于我们统一了事件源EventData,委托参数保证了一致性,所以实现了委托的共用。

    修改我们的猫类

     public class Cat:IEventHandler<SelfDefineEventData>
        {
            /// <summary>
            /// 事件的处理逻辑
            /// </summary>
            /// <param name="mouseName"></param>
            public void CatchMouse(string mouseName)
            {
                Console.WriteLine("小猫说:
    ");
                Console.WriteLine("我抓住了{0}",mouseName);
            }
    
            public void Handle(SelfDefineEventData eventData)
            {
               //处理猫捉老鼠
               if(!string.IsNullOrEmpty(eventData.MouseName))
                {
                    Console.WriteLine("小猫说:
    ");
                    Console.WriteLine("我抓住了{0}", eventData.MouseName);
    
                }
                //处理猫吃鱼
                if (eventData.FishWeight != 0)
                {
                    Console.WriteLine("小猫说:
    ");
                    Console.WriteLine("我吃了{0}斤鱼", eventData.FishWeight);
                }
            }
        }

    猫捉老鼠和猫吃鱼

      static void Main(string[] args)
            {
    
                Cat c1 = new Cat();
                Mouse m1 = new Mouse("1号老鼠");
                Fish f1 = new Fish(20);
                //订阅事件
                m1.selfEventHandler += c1.Handle;
                //这边的事件变量定义的名字一样了,可以用不同的名字区分,这是事件不是委托
                f1.selfEventHandler += c1.Handle;
                //触发事件
                m1.Comming(m1.Name);
                f1.Comming(f1.Weight);
                Console.ReadLine();
            }

    从上面的代码可以看出我们统一了事件处理逻辑为Handle

    上面的代码从逻辑上来看,每次都是同时触发执行了所有的注册事件,那么如果我需要只触发一个事件????那么我们只需要对传入参数EventData进行判断即可,方案有两个:

    1.直接在SelfDefineEventData中将EventSource赋值为Mouse和Fish,然后在Handle方法中判断EventSource的类型

    这种方式是承接上面的实现代码的,突然发现一个问题,上面的代码虽然可以跑的通,但是有一种过度封装的感觉,本来每一个XXEventData都是继承自EventData,都是一个独立的模块,所以我们上面的代码违背了单一性原则

    2.分别定义两个EventData:FishEventData和MouseEventData,然后在Handle中判断eventData的类型,所以我们采用这种方法,并将前面的代码重新修改

    定义事件源

        public class MouseEventData : EventData
        {
            public string MouseName { get; set; }
        }
        public class FishEventData : EventData
        {
            public float FishWeight { get; set; }
        }

    修改鱼和老鼠类

      public  class Mouse
        {
            
          
            //声明事件委托
            public event MouserEventHandler selfEventHandler;
            public Mouse(string name)
            {
                this.Name = name;
            }
            public void Comming(string name )
            {
                Console.WriteLine("老鼠说:
    ");
    
                Console.WriteLine("我的名字是{0},I am Comming!", name);
                //触发事件
                selfEventHandler(new MouseEventData() { MouseName = name});
            }
            public string Name { get; set; }
            
        }
        public class Fish
        {
            //声明事件委托
            public event FishEventHandler selfEventHandler;
            public Fish(float weight)
            {
                this.Weight = weight;
            }
            public void Comming(float weight)
            {
                Console.WriteLine("小鱼说:
    ");
    
                Console.WriteLine("我的重量是{0},I am Comming!", weight);
                //触发事件
                selfEventHandler(new FishEventData() { FishWeight = weight });
            }
            public float Weight { get; set; }
        }

    定义不同的事件委托

        public delegate void MouserEventHandler(MouseEventData eventData );
        public delegate void FishEventHandler(FishEventData eventData);

    修改猫类

     public class Cat:IEventHandler<IEventData>
        {
           
            public void Handle(IEventData eventData)
            {
                if (eventData is MouseEventData)
                {
                   var mouseData = (MouseEventData)eventData;
                    //处理猫捉老鼠
                    if (!string.IsNullOrEmpty(mouseData.MouseName))
                    {
                        Console.WriteLine("小猫说:
    ");
                        Console.WriteLine("我抓住了{0}", mouseData.MouseName);
    
                    }
                }
                if (eventData is FishEventData)
                {
                    var fishData = (FishEventData)eventData;
                    //处理猫吃鱼
                    if (fishData.FishWeight != 0)
                    {
                        Console.WriteLine("小猫说:
    ");
                        Console.WriteLine("我吃了{0}斤鱼", fishData.FishWeight);
                    }
                }
            }
        }

    重新实现猫捉老鼠

      class Program
        {
            static void Main(string[] args)
            {
    
                Cat c1 = new Cat();
                Mouse m1 = new Mouse("1号老鼠");
               
                //订阅事件
                m1.selfEventHandler += c1.Handle;
                //触发事件
                m1.Comming(m1.Name);
               
                Console.ReadLine();
            }
          
        }

    发现一个问题

    原来父类参数事件可以赋值给子类参数事件上,哈哈

    到此为止,我们的发布订阅模式已经完成,所以每次有新的需求,只需要分别定制XXEventData,体现了一种模块化,单一性的思想,同时将事件处理进行统一,体现了封装统一的思想,两者结合恰到好处。

    参考文章:事件知多少https://www.jianshu.com/p/22fbe7a7c120

  • 相关阅读:
    pat 甲级 1065. A+B and C (64bit) (20)
    pat 甲级 1064. Complete Binary Search Tree (30)
    pat 甲级 1010. Radix (25)
    pat 甲级 1009. Product of Polynomials (25)
    pat 甲级 1056. Mice and Rice (25)
    pat 甲级 1078. Hashing (25)
    pat 甲级 1080. Graduate Admission (30)
    pat 甲级 团体天梯 L3-004. 肿瘤诊断
    pat 甲级 1099. Build A Binary Search Tree (30)
    Codeforce 672B. Different is Good
  • 原文地址:https://www.cnblogs.com/XZhao/p/8698988.html
Copyright © 2011-2022 走看看