承接上一篇时间总线的学习,在上一篇中我们实现了取消显式注册事件的方式,采用使用反射的方式。这样的好处可以解除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()[];
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

ABP之事件总线(3)的更多相关文章

  1. ABP之事件总线(5)

    前面已经对Castle Windsor的基本使用进行了学习,有了这个基础,接下来我们将把我们的事件总线再次向ABP中定义的事件总线靠近.从源码中可以知道在ABP中定义了Dictionary,存放三种类 ...

  2. ABP之事件总线(4)

    在上一篇的随笔中,我们已经初步完成了EventBus,但是EventBus中还有诸多的问题存在,那么到底有什么问题呢,接下来我们需要看一看ABP中的源码是如何定义EventBus的. 1.第一个点 在 ...

  3. ABP之事件总线(1)

    什么是事件总线呢?官方的文档说,它是一个单例对象,由其他的类共同拥有,可以用来触发和处理事件.这个东西确实比较陌生,为什么要使用事件总线,或者说事件总线的优势是什么???首先我们可以明确的是,事件总线 ...

  4. ABP的事件总线和领域事件(EventBus & Domain Events)

    http://www.aspnetboilerplate.com/Pages/Documents/EventBus-Domain-Events EventBus EventBus是个单例,获得Even ...

  5. ABP之事件总线(2)

    在上一篇文章中,我们复习了一下事件的经典的发布订阅模式,同时对是事件源和时间处理逻辑进行抽象统一,用起来也没有问题.但是还是有很多的问题,比如说我们Handle方法其实是违背了单一性的原则的,里面混杂 ...

  6. [Abp 源码分析]九、事件总线

    0.简介 事件总线就是订阅/发布模式的一种实现,本质上事件总线的存在是为了降低耦合而存在的. 从上图可以看到事件由发布者发布到事件总线处理器当中,然后经由事件总线处理器调用订阅者的处理方法,而发布者和 ...

  7. ABP理论学习之事件总线和领域事件

    返回总目录 本篇目录 事件总线 定义事件 触发事件 处理事件 句柄注册 取消注册 在C#中,我们可以在一个类中定义自己的事件,而其他的类可以注册该事件,当某些事情发生时,可以通知到该类.这对于桌面应用 ...

  8. ABP官方文档翻译 3.7 领域事件(事件总线)

    领域事件(事件总线) 事件总线 注入IEventBus 获取默认实例 定义事件 预定义事件 处理异常 实体更改 触发事件 处理事件 处理基础事件 处理者异常 处理多个事件 注册处理者 自动 手动 取消 ...

  9. ABP EventBus(事件总线)

    事件总线就是订阅/发布模式的一种实现    事件总线就是为了降低耦合 1.比如在winform中  到处都是事件 触发事件的对象  sender 事件的数据    e 事件的处理逻辑  方法体 通过E ...

随机推荐

  1. ArrayList vs LinkedList 空间占用

    空间占用上,ArrayList完胜 看下两者的内存占用图   这三个图,横轴是list长度,纵轴是内存占用值.两条蓝线是LinkedList,两条红线是ArrayList,可以看到,LinkedLis ...

  2. WCF系列(六) - WCF安全系列(一) - basicHttpBinding

    绑定可指定在与终结点通话时所使用的通信机制,并指示如何连接到终结点.绑定由一些元素组成,这些元素指定如何对 Windows Communication Foundation (WCF) 通道进行分层以 ...

  3. [转]MyEclipse内存不足问题

    1.修改eclipse.ini 在Myeclipse安装目录下G:\MyEclipse8.5\Genuitec\MyEclipse 8.5有一个myeclipse.ini配置文件,设置如下: -vma ...

  4. 【转载】JAVA基础:注解

    原文:https://www.cnblogs.com/xdp-gacl/p/3622275.html#undefined 一.认识注解 注解(Annotation)很重要,未来的开发模式都是基于注解的 ...

  5. keras 文本分类 LSTM

    首先,对需要导入的库进行导入,读入数据后,用jieba来进行中文分词 # encoding: utf-8 #载入接下来分析用的库 import pandas as pd import numpy as ...

  6. Django TemplateDoesNotExist

    在联系Django的时候,启动正常,我在浏览器上输入URL地址后报错 TemplateDoesNotExist at /test/ 解决方案 默认这里是空的,这里我们填上我们静态文件的地址

  7. Spring Boot 应用 发布到Docker

    Spring Boot 应用 先把命令行切换到Maven项目的根目录 E:\gitCode\galaxyguardians 通过mvn clean package命令打包应用程序 ,在E:\gitCo ...

  8. netbeans增加yii的代码提示

    在NetBeans中创建PHP项目 NetBeans目前还没有直接支持Yii Framework或Yii相关的插件,但是可以通过其他方式来实现Yii的代码自动提示. 首先,打开新建项目向导,创建一个“ ...

  9. 【iCore1S 双核心板_FPGA】例程五:Signal Tapll 实验——逻辑分析仪

    核心代码: //--------------------Module_Signal_TapII-------------------// module Signal_TapII( input CLK_ ...

  10. Guava Lists.transform踩坑小记<转>

    1.问题提出 1.前段时间在项目中用到Lists.transform返回的List,在对该list修改后发现修改并没有反映在结果里,研究源码后发现问题还挺大.下面通过单步调试的结果来查看Guava L ...