ABP之事件总线(1)

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

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

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

sender:触发事件的对象

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

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

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

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

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

以猫捉老鼠的故事为例子

 public class Cat
    {
        /// <summary>
        /// 事件的处理逻辑
        /// </summary>
        /// <param name="mouseName"></param>
        public void CatchMouse(string mouseName)
        {
            Console.WriteLine("小猫说:\n");
            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("老鼠说:\n");

            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("老鼠说:\n");

            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("小鱼说:\n");

            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("小猫说:\n");
            Console.WriteLine("我抓住了{0}",mouseName);
        }

        public void Handle(SelfDefineEventData eventData)
        {
           //处理猫捉老鼠
           if(!string.IsNullOrEmpty(eventData.MouseName))
            {
                Console.WriteLine("小猫说:\n");
                Console.WriteLine("我抓住了{0}", eventData.MouseName);

            }
            //处理猫吃鱼
            if (eventData.FishWeight != 0)
            {
                Console.WriteLine("小猫说:\n");
                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("老鼠说:\n");

            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("小鱼说:\n");

            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("小猫说:\n");
                    Console.WriteLine("我抓住了{0}", mouseData.MouseName);

                }
            }
            if (eventData is FishEventData)
            {
                var fishData = (FishEventData)eventData;
                //处理猫吃鱼
                if (fishData.FishWeight != 0)
                {
                    Console.WriteLine("小猫说:\n");
                    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

 

转载于:https://www.cnblogs.com/XZhao/p/8698988.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值