• C#总结(六)EventBus事件总线的使用-自己实现事件总线


    在C#中,我们可以在一个类中定义自己的事件,而其他的类可以订阅该事件,当某些事情发生时,可以通知到该类。这对于桌面应用或者独立的windows服务来说是非常有用的。但对于一个web应用来说是有点问题的,因为对象都是在web请求中创建的,而且这些对象生命周期都很短,因而注册某些类的事件是很困难的。此外,注册其他类的事件会使得类紧耦合。事件总线便可以用来解耦并重复利用应用中的逻辑。

    事件总线带来的好处和引入的问题

    好处比较明显,就是独立出一个发布订阅模块,调用者可以通过使用这个模块,屏蔽一些线程切换问题,简单地实现发布订阅功能。

    坏处可能比较隐晦,但这些需要足够引起我们的重视

    • 大量的滥用,将导致逻辑的分散,出现问题后很难定位。
    • 没办法实现强类型,在编译的时候就发现问题。
    • 代码可读性有些问题,IDE无法识别这些协议,对IDE不友好。
    总得来说,如果项目里面有大量的事件交互,那么还是可以通过EventBus来实现,否则还是推荐自己在模块内部实现观察者模式。
     

    示例代码

    所以今天介绍一个简单的事件总线,它是事件发布订阅模式的实现,让我们能在领域驱动设计(DDD)中以事件的弱引用本质对我们的模块和领域边界很好的解耦设计。

    目前,所有的源代码已经提交到github 上,地址:https://github.com/weizhong1988/Weiz.EventBus

     程序目录结构如下:

    事件总线

    事件总线是被所有触发并处理事件的其他类共享的单例对象。要使用事件总线,首先应该获得它的一个引用。下面有两种方法来处理:

    订阅事件

    触发事件之前,应该先要定义该事件。EventBus为我们提供了Subscribe 方法来订阅事件:

            public void Subscribe<TEvent>(IEventHandler<TEvent> eventHandler) where TEvent : IEvent
            {
                //同步锁
                lock (_syncObject)
                {
                    //获取领域模型的类型
                    var eventType = typeof(TEvent);
                    //如果此领域类型在事件总线中已注册过
                    if (_dicEventHandler.ContainsKey(eventType))
                    {
                        var handlers = _dicEventHandler[eventType];
                        if (handlers != null)
                        {
                            handlers.Add(eventHandler);
                        }
                        else
                        {
                            handlers = new List<object>
                            {
                                eventHandler
                            };
                        }
                    }
                    else
                    {
                        _dicEventHandler.Add(eventType, new List<object> { eventHandler });
                    }
                }
            }

    所以的事件都集成自IEvent,该类包含了类处理事件需要的属性。

     var sendEmailHandler = new UserAddedEventHandlerSendEmail();
     var sendMessageHandler = new UserAddedEventHandlerSendMessage();
     var sendRedbagsHandler = new UserAddedEventHandlerSendRedbags();
     Weiz.EventBus.Core.EventBus.Instance.Subscribe(sendEmailHandler);
     Weiz.EventBus.Core.EventBus.Instance.Subscribe(sendMessageHandler);
     //Weiz.EventBus.Core.EventBus.Instance.Subscribe<UserGeneratorEvent>(sendRedbagsHandler);
     Weiz.EventBus.Core.EventBus.Instance.Subscribe<OrderGeneratorEvent>(sendRedbagsHandler);

    发布事件

    对于事件源,则可以通过Publish 方法发布事件。触发一个事件很简单,如下所示:

         public void Publish<TEvent>(TEvent tEvent, Action<TEvent, bool, Exception> callback) where TEvent : IEvent
            {
                var eventType = typeof(TEvent);
                if (_dicEventHandler.ContainsKey(eventType) && _dicEventHandler[eventType] != null &&
                    _dicEventHandler[eventType].Count > 0)
                {
                    var handlers = _dicEventHandler[eventType];
                    try
                    {
                        foreach (var handler in handlers)
                        {
                            var eventHandler = handler as IEventHandler<TEvent>;
                            eventHandler.Handle(tEvent);
                            callback(tEvent, true, null);
                        }
                    }
                    catch (Exception ex)
                    {
                        callback(tEvent, false, ex);
                    }
                }
                else
                {
                    callback(tEvent, false, null);
                }
            }

    下面是发布事件的调用:

                var orderGeneratorEvent = new OrderGeneratorEvent { OrderId = Guid.NewGuid() };
    
                System.Console.WriteLine("{0}下单成功", orderGeneratorEvent.OrderId);
              
                Weiz.EventBus.Core.EventBus.Instance.Publish(orderGeneratorEvent, CallBack);

    定义处理事件

    要处理一个事件,应该要实现IEventHandler接口,如下所示:

        /// <summary>
        /// send email
        /// </summary>
        public class UserAddedEventHandlerSendEmail : IEventHandler<UserGeneratorEvent>
        {
    
            public void Handle(UserGeneratorEvent tEvent)
            {
                System.Console.WriteLine(string.Format("{0}的邮件已发送", tEvent.UserId));
            }
        }

    处理多事件

    在一个单一的处理句柄中,可以处理多个事件。这时,你应该为每个事件实现IEventHandler。比如:

        /// <summary>
        /// red bags.
        /// </summary>
        public class UserAddedEventHandlerSendRedbags : IEventHandler<UserGeneratorEvent>,IEventHandler<OrderGeneratorEvent>
        {
            public void Handle(OrderGeneratorEvent tEvent)
            {
                System.Console.WriteLine(string.Format("{0}的下单红包已发送", tEvent.OrderId));
            }
    
            public void Handle(UserGeneratorEvent tEvent)
            {
                System.Console.WriteLine(string.Format("{0}的注册红包已发送", tEvent.UserId));
            }
        }

    最后

    以上,就把事件总线介绍完了,完整的代码,请到github 上下载,这个只是EventBus 的简单实现,各位可以根据自己的实际场景和需求,优化修改。

  • 相关阅读:
    .Net Core微服务——Ocelot(2):集成Consul 老马
    .NET 微服务——CI/CD(1):Jenkins+Gitee自动构建 老马
    .Net Core——用SignalR撸个游戏 老马
    JUC之线程间的通信
    SpringBoot文章合集
    JUC之线程间定制化通信
    JUC之集合中的线程安全问题
    JUC文章合集
    JUC之Lock接口以及Synchronized回顾
    JUC概述
  • 原文地址:https://www.cnblogs.com/zhangweizhong/p/8544158.html
Copyright © 2020-2023  润新知