用例:假设公司发布了一个公告 需要通过短信 和 邮件分别2种方式 通知员工

1:首先我们建立领域模型

    /// <summary>
/// 领域核心基类
/// </summary>
public abstract class Core
{
public string Id { set; get; } = Guid.NewGuid().ToString();
} public interface ICore
{ }

2:消息模型

 /// <summary>
/// 通知的领域模型
/// </summary>
public class Notice : Core
{
/// <summary>
/// 通知内容
/// </summary>
public string Message { set; get; } /// <summary>
/// 通知发送时间
/// </summary>
public DateTime DateTime { set; get; } = DateTime.Now;
}

这个时候我们会想到 建立2个服务类 一个是SmsService 和 EmailService服务 分别用来发送短信和Email

    public class EmailService
{
public EmailService() { } public EmailService(Entity.Notice notice) => Console.WriteLine($"邮件通知:{notice.Message} 发送时间:{notice.DateTime}"); }
  public class SmsService
{
public SmsService() { } public SmsService(Entity.Notice notice) => Console.WriteLine($"短信通知:{notice.Message} 发送时间:{notice.DateTime}");
}
static void Main(string[] args)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); Entity.Notice notice = new Entity.Notice() { Message ="明天加班 加班 加班 ~!!! 重要的事情说三遍" };
new Service.EmailService(notice);
new Service.SmsService(notice);
}
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance) 
这里主要是因为.NET CORE中文输出会导致乱码 需要加上编码配置。也可以换上非Core平台
看看运行后的效果
运行后的效果似乎已经满足了我们的需求 公司公告分别以2种方式发送出去了
这样就带来了一个问题 如果未来社交发展需要多平台发送通知呢。。假设这里有还有QQ 默默 探探 钉钉 微信 等等
这一系列的消息推送方式 那我们的代码里是不是这样 ?
            Entity.Notice notice = new Entity.Notice() { Message ="明天加班 加班 加班 ~!!! 重要的事情说三遍" };
new Service.EmailService(notice);
new Service.SmsService(notice);
new Service.QQService(notice);
new Service.WeiXinService(notice);
new Service.MomoService(notice);
new Service.DingDService(notice);
new Service.TanTanService(notice);

这样写显然看起来是一个非常蛋疼的事。仔细想想这一系列的消息推送不变的是 事件消息源 也就是Notice对象

并且所有发送消息的方法都是被动的接收这个对象 , 这样我们就可以把2者的关系理解成 消息源是发布者,

具体处理消息发送的是订阅者,从而我们换一个思路去改造之前的方法

 public interface IEventHandler<T> where T : Entity.Core
{
/// <summary>
/// 订阅对象的具体实现
/// </summary>
/// <param name="entity"></param>
void Handler(T entity);
}

定义一个泛型接口,所有订阅者必须实现这个接口

    /// <summary>
/// Email形式处理
/// </summary>
public class EmailEventHandler : IEventHandler<Entity.Notice>
{
public void Handler(Notice notice)
{
Console.WriteLine($"邮件通知:{notice.Message} 发送时间:{notice.DateTime}");
}
}
 /// <summary>
/// 短信形式处理
/// </summary>
public class SmsEventHandler : IEventHandler<Entity.Notice>
{
public void Handler(Notice notice)
{
Console.WriteLine($"短信通知:{notice.Message} 发送时间:{notice.DateTime}");
}
}

这是改造的第一步,改造的目的是让它能以一种自动的方式处理,而不像之前一样需要一个一个对象的new出来,

做到能像看电视一样,只要你打开电视就能收看到传输过来的画面

定义一个消息总线的接口 用来管理 订阅者的注册 以及消息的发布

  public interface IBus
{
/// <summary>
/// 默认订阅所有事件消息 启动时调用
/// </summary>
void SubscribeAll(); /// <summary>
/// 订阅
/// </summary>
/// <param name="type"></param>
/// <param name="data"></param>
void Subscribe(Type type, object data); /// <summary>
/// 发布订阅
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="eneity"></param>
void Publish<T>(T eneity) where T : Entity.Core;
}

定义一个BusManager来实现 IBus

        private BusManager()
{
if (_instance == null)
Bus = new BusService();
} private static object _lock = new object(); private static BusManager _instance; public static IBus Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
_instance = new BusManager();
}
}
return _instance;
}
}

/// <summary>
/// 主要是针对 事件源和具体的事件处理注册关系。ConcurrentDictionary保证了线程的安全
/// </summary>
private static ConcurrentDictionary<Type, List<object>> _dicHandlers = new ConcurrentDictionary<Type, List<object>>();
/// <summary>
/// 判断type是否是abs的实现类或者子类/
/// .net Core中对反射进行了单独的处理 反射后一些详细属性都要通过GetTypeInfo获取 原对象只保留了一些基本属性
/// </summary>
/// <param name="type"></param>
/// <param name="abs"></param>
/// <returns></returns>
private bool IsAssignableFrom(Type type, Type abs)
{
///
if ((abs.GetTypeInfo().IsAbstract || abs.GetTypeInfo().IsInterface) && abs.IsAssignableFrom(type))
return true;
else
{ if (type.GetInterfaces().Any(o => o.GetTypeInfo().IsGenericType && o.GetGenericTypeDefinition() == abs))
return true;
}
return false;
} /// <summary>
/// 判断2个类型是否相同
/// </summary>
private Func<object, object, bool> _Equals = (o1, o2) =>
{
return o1.GetType() == o2.GetType();
};

上述方法都是为了实现IBus接口所做的铺垫 如有.Net Core反射疑问的自行Bing

        public void Subscribe(Type type, object data)
{
lock (_lock)
{
if (_dicHandlers.ContainsKey(type))
{
var _handlers = _dicHandlers[type];
if (!_handlers.Any(o => _Equals(o, data)))
_handlers.Add(data);
}
else
{
_dicHandlers[type] = new List<object>() { data };
}
}
}

实现的单个对象的加载 Key为具体的发布对象类型, Value是具体的订阅者的行为实现集合,这里是多个订阅者的实现


ConcurrentDictionary 里的关系

  /// <summary>
/// 初始化 默认的所有实现都订阅事件
/// </summary>
public void SubscribeAll()
{
///加载程序集,具体的你也可以加载项目路径下所有的dll或者exe
var assembly = Assembly.Load(new AssemblyName("ConsoleApp2"));
assembly.GetTypes().Where(x => x.GetTypeInfo().IsClass && !x.GetTypeInfo().IsAbstract && !x.GetTypeInfo().IsInterface).ToList().ForEach(x =>
{
if (IsAssignableFrom(x, typeof(IEventHandler<>)))
{
///反射创建对象
var entity = Activator.CreateInstance(x);
///获得到泛型参数的类型
var key = x.GetInterfaces().FirstOrDefault().GetGenericArguments().FirstOrDefault();
this.Subscribe(key, entity);
}
}); }

事件源的发布


 /// <summary>
/// 事件源的发布
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="eneity"></param>
public void Publish<T>(T eneity) where T : Core
{
var type = eneity.GetType();
if (_dicHandlers.ContainsKey(type) && _dicHandlers[type] != null)
{
_dicHandlers[type].ForEach(o =>
{
var eve = o as IEventHandler<T>;
eve.Handler(eneity);
});
}
}
上面的所有代码基本上就完成了一个简单的事件总线驱动的模式,
试试最终的运行效果。
是不是感觉很神奇,这就是单纯自己理解的事件总线模式。

 

EventBus 事件总线之我的理解的更多相关文章

  1. Guava - EventBus(事件总线)

    Guava在guava-libraries中为我们提供了事件总线EventBus库,它是事件发布订阅模式的实现,让我们能在领域驱动设计(DDD)中以事件的弱引用本质对我们的模块和领域边界很好的解耦设计 ...

  2. EventBus(事件总线)

    EventBus(事件总线) Guava在guava-libraries中为我们提供了事件总线EventBus库,它是事件发布订阅模式的实现,让我们能在领域驱动设计(DDD)中以事件的弱引用本质对我们 ...

  3. dhroid - eventbus 事件总线

    你听过onClick 事件,onItemClick 事件,事件总线不一定听过吧, eventbus 事件总线也是一个编程思想,为什么要设计EventBus了,因为他是领域驱动设计中比不可少的模块,它承 ...

  4. EventBus事件总线

    EventBus事件总线的使用-自己实现事件总线   在C#中,我们可以在一个类中定义自己的事件,而其他的类可以订阅该事件,当某些事情发生时,可以通知到该类.这对于桌面应用或者独立的windows服务 ...

  5. EventBus 事件总线 案例

    简介 地址:https://github.com/greenrobot/EventBus EventBus是一个[发布 / 订阅]的事件总线.简单点说,就是两人[约定]好怎么通信,一人发布消息,另外一 ...

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

    在C#中,我们可以在一个类中定义自己的事件,而其他的类可以订阅该事件,当某些事情发生时,可以通知到该类.这对于桌面应用或者独立的windows服务来说是非常有用的.但对于一个web应用来说是有点问题的 ...

  7. Android 开发 框架系列 EventBus 事件总线

    介绍 GitHub:https://github.com/greenrobot/EventBus 先聊聊EventBus 线程总线是干什么的,使用环境,优点.缺点. 干什么的? 一句话,简单统一数据传 ...

  8. EventBus事件总线(牛x版)

    事件总线: public interface IEventBus { void Trigger<TEvent>(TEvent eventData, string topic = null) ...

  9. Orchard EventBus 事件总线及 IEventHandler作用

    事件总线接口定义: public interface IEventBus : IDependency { IEnumerable Notify(string messageName, IDiction ...

随机推荐

  1. collections 模块:更多数据结构

  2. TCP:三次握手、四次握手、backlog及其他

    TCP是什么 首先看一下OSI七层模型: 然后数据从应用层发下来,会在每一层都加上头部信息进行封装,然后再发送到数据接收端,这个基本的流程中每个数据都会经过数据的封装和解封的过程,流程如下图所示: 在 ...

  3. Javascript编码规范,好的代码从书写规范开始,增强代码的可读性,可维护性,这是相当重要的!

    1. 前言 JavaScript在百度一直有着广泛的应用,特别是在浏览器端的行为管理.本文档的目标是使JavaScript代码风格保持一致,容易被理解和被维护. 虽然本文档是针对JavaScript设 ...

  4. 两强相争,鹿死谁手 — JQuery中的Ajax与AngularJS中的$http

    一.JQuery与AngularJS 首先,先简单的了解一下JQuery与AngularJS.从源头上来说,两者都属于原生JS所封装成的库,两种为平行关系. 二.Ajax请求与数据遍历打印 这里是Aj ...

  5. Vue单页式应用(Hash模式下)实现微信分享

    前端微信分享的基本步骤: 一.绑定域名: 先登录微信公众平台进入"公众号设置"的"功能设置"里填写"JS接口安全域名".这个不多说,微信开发 ...

  6. 网络编程应用:基于UDP协议【实现聊天程序】--练习

    要求: 使用UDP协议实现一个聊天程序 代码: 发送端: package UDP聊天程序; import java.io.IOException; import java.net.DatagramPa ...

  7. 分享两个网址,一个是使用mssql自带的跟踪工具和分析工具

    http://www.cnblogs.com/Fooo/archive/2013/02/19/2916789.html 使用mssql自带的跟踪工具和分析工具 http://blog.csdn.net ...

  8. Yii2项目实现Markdown功能 在线Markdown编辑器

    版权声明:本文为博主原创文章,欢迎扩散,扩散请务必注明出处. Yii中添加MarkDown编辑器 主要使用了两个网页Markdown编辑器,都带预览功能. 1,ijackua/yii2-lepture ...

  9. JavaFx TableView疑难详解

    TableView是个十分有用的控件,适应性和灵活性非常强,可以对它进行任意的修改,比如界面样式.功能.本文将从一步步提问的方式讲解TableView 创建已知列的TableView 已知列的表格的创 ...

  10. 高性能队列Disruptor系列1--传统队列的不足

    在前一篇文章Java中的阻塞队列(BlockingQueue)中介绍了Java中的阻塞队列.从性能上我们能得出一个结论:数组优于链表,CAS优于锁.那么有没有一种队列,通过数组的方式实现,而且采用无锁 ...