ABP之事件总线(3)
承接上一篇时间总线的学习,在上一篇中我们实现了取消显式注册事件的方式,采用使用反射的方式。这样的好处可以解除Publisher和Scriber的显式依赖,但是问题又来了,因为我们只有Publisher和Scriber,而且我们又要实现这两者的通信,那么无论使用那种方式都不可能解除两者的依赖关系,反射也只是将依赖关系进行隐藏罢了。而且还有一个至关重要的问题,那个我们每个Publisher都得去写那么一坨的反射代码,而且这些代码其实都是公用的。写到这里,突然感觉要解决这两个问题,有一种想法呼之欲出,那就是我们的主题——事件总线,这个中间大佬来帮我们统一的管理反射代码,帮我们统一的管理EventData和EventHandler的Map关系,然后通过反射的方式可以调用。所有的Publisher(EventData)和Scriber(EventHandler)不再耦合,全部跟事件总线通信。
具体的关系图
接下来我们就将上一篇的接触耦合的代码统一的放在EventBus里,实现我们自己的第一个EventBus
public class EventBus:IEventBus { private EventBus() { mapDic = new ConcurrentDictionary<Type, List<Type>>(); MapEvent2Handler(); } //EventBus单例模式 public static EventBus Default = new EventBus(); //存储EventData和EventHandle的映射关系(没有存放handler的实例而是存放metaData,然后通过反射调用) private ConcurrentDictionary<Type, List<Type>> mapDic;/// <summary> /// 自动实现注册 /// </summary> private void MapEvent2Handler() { var assembly = Assembly.GetExecutingAssembly(); var types = assembly.GetTypes(); foreach (var type in types) { //第一层过滤出继承自IEventHandler接口的类型 if (typeof(IEventHandler).IsAssignableFrom(type)) { Type handlerType = type.GetInterface("IEventHandler`1");//获取这个类是实现的IEventHandler<TEventData>接口 //过滤掉IEventHandler类型 if (handlerType != null) { Type dataType= handlerType.GetGenericArguments()[0]; if (mapDic.Keys.Contains(dataType)) { mapDic[dataType].Add(type); } else { mapDic[dataType] = new List<Type>() { type }; } } } } } /// <summary> /// 触发调用处理事件 /// </summary> /// <typeparam name="TEventData"></typeparam> /// <param name="eventData"></param> public void Trigger<TEventData>(TEventData eventData) where TEventData : IEventData { // var dataType = typeof(TEventData); var dataType = eventData.GetType(); var handlerTypes = mapDic[dataType]; foreach (var handlerType in handlerTypes) { var methodInfo = handlerType.GetMethod("Handle"); if (methodInfo != null) { // var handler = Assembly.GetExecutingAssembly().CreateInstance(handlerType.FullName); var handler = Activator.CreateInstance(handlerType); methodInfo.Invoke(handler,new object[] { eventData}); } } } }
此时我们触发事件再也不需要事件委托来触发了直接使用事件总线来触发,彻底简化的添加事件的代码,彻底实现了Publisher和Scriber的解除耦合,事件委托已经从我们的视野中渐行渐远了~~~~~~~~
Mouse类中的Come方法中就不需要事件委托了
public void Come() { //MouseEventHandler(new MouseEventData() { Name=this.Name}); EventBus.Default.Trigger(new MouseEventData() { Name=this.Name}); }
猫捉老鼠
static void Main(string[] args) { Mouse m = new Mouse("老鼠1号"); // m.MouseEventHandler += new CatchEventHandler().Handle; m.Come(); Console.Read(); }
我们这个超级强大的EventBus,由于借助了反射这一无敌的技能,实现了未卜先知的能力,可以自动将我们程序集中的所有的EventHandler和EventData进行绑定,但是过于自动,过刚易折~~~~~~,现在我需要只是触发某些事件的逻辑,我还想取消某些事件。。。。。咋办???废话,既然你不想执行,那么你就别去实现这些逻辑啊!但是我想在某些情况下触发事件,某些情况下不触发事件逻辑呢???。。。。。。。好吧,看来我强大的EventBus需要释放权利给用户。我们最基本的EventBus需要开放那些接口呢?
(1)用户手动注册的接口 (2)用户取消注册事件的接口(3)触发我们时间逻辑的接口
IEventBus
public interface IEventBus { /// <summary> /// 注册EventData和Handler的映射 /// </summary> /// <typeparam name="TEventData"></typeparam> /// <typeparam name="THandler"></typeparam> void Register<TEventData>(Type handler) where TEventData : IEventData; void Register(Type eventType, Type handler); /// <summary> /// 取消注册 /// </summary> /// <typeparam name="TEventData"></typeparam> /// <param name="handler"></param> void Unregister<TEventData>(Type handler) where TEventData : IEventData; void Unregister(Type eventType, Type handler); /// <summary> /// 触发事件 /// </summary> /// <typeparam name="TEventData"></typeparam> /// <param name="eventData"></param> void Trigger<TEventData>(TEventData eventData) where TEventData : IEventData; }
EventBus
public class EventBus:IEventBus { private EventBus() { mapDic = new ConcurrentDictionary<Type, List<Type>>(); } //EventBus单例模式 public static EventBus Default = new EventBus(); //存储EventData和EventHandle的映射关系(没有存放handler的实例而是存放metaData,然后通过反射调用) private ConcurrentDictionary<Type, List<Type>> mapDic; public void Register<TEventData>(Type handlerType) where TEventData : IEventData { //将数据存储到mapDic var dataType = typeof(TEventData); Register(dataType,handlerType); } public void Register(Type dataType, Type handlerType) { if (mapDic.Keys.Contains(dataType)) { if (!mapDic[dataType].Contains(handlerType)) { mapDic[dataType].Add(handlerType); } } else { mapDic[dataType] = new List<Type>() { handlerType }; } } public void Unregister<TEventData>(Type handler) where TEventData : IEventData { var dataType = typeof(TEventData); Unregister(dataType, handler); } public void Unregister(Type eventType, Type handler) { if (mapDic.Keys.Contains(eventType)) { if (mapDic[eventType].Contains(handler)) { mapDic[eventType].Remove(handler); } } } /// <summary> /// 触发调用处理事件 /// </summary> /// <typeparam name="TEventData"></typeparam> /// <param name="eventData"></param> public void Trigger<TEventData>(TEventData eventData) where TEventData : IEventData { // var dataType = typeof(TEventData); var dataType = eventData.GetType(); var handlerTypes = mapDic[dataType]; foreach (var handlerType in handlerTypes) { var methodInfo = handlerType.GetMethod("Handle"); if (methodInfo != null) { // var handler = Assembly.GetExecutingAssembly().CreateInstance(handlerType.FullName); var handler = Activator.CreateInstance(handlerType); methodInfo.Invoke(handler,new object[] { eventData}); } } } }
猫捉老鼠
static void Main(string[] args) { EventBus.Default.Register(typeof(MouseEventData), typeof(CatchEventHandler)); Mouse m = new Mouse("老鼠1号"); // m.MouseEventHandler += new CatchEventHandler().Handle; m.Come(); Console.Read(); }
到现在为止,我们事件总线的原型就已经出来了,ABP的源码中EventBus的实现和上面类似的,就是方法多一些,我们现在的EventBus问题还是有很多的。
参考:
ABP EventBus:https://github.com/aspnetboilerplate/aspnetboilerplate/blob/dev/src/Abp/Events/Bus/EventBus.cs
事件总线知多少:https://www.jianshu.com/p/22fbe7a7c120
DDD事件总线的实现:http://www.cnblogs.com/dehai/p/4887998.html