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

  • 相关阅读:
    [LeetCode] Dungeon Game
    [LeetCode] Maximal Rectangle
    [LeetCode] Scramble String -- 三维动态规划的范例
    [LeetCode] Palindrome Partitioning II
    [LeetCode] Palindrome Partitioning
    [LeetCode] Interleaving String
    [LeetCode] Longest Valid Parentheses -- 挂动态规划羊头卖stack的狗肉
    JPA将查询结果转换为DTO对象
    EOS签名R值过大导致报错"is_canonical( c ): signature is not canonical"
    比特币中P2SH(pay-to-script-hash)多重签名的锁定脚本和解锁脚本
  • 原文地址:https://www.cnblogs.com/XZhao/p/8698988.html
Copyright © 2011-2022 走看看