用例:假设公司发布了一个公告 需要通过短信 和 邮件分别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. javascript基础-语法

    代表此属性ECMAScript 5(一般IE9+)才支持. 图解:—— 图解: undefined,null,NaN Infinity ECMAScript 5禁止修改 每个函数都是Function的 ...

  2. MD5加密算法(信息摘要算法)、Base64算法

    1 什么是MD5 信息摘要算法,可以将字符进行加密,每个加密对象在进行加密后都是等长的 应用场景:将用户密码经过MD5加密后再存储到数据库中,这样即使是超级管理员也没有能力知道用户的具体密码是多少:因 ...

  3. Java之进程与线程练习

    1.设计一个线程类:创建3个子线程,每个线程分别打印数字,分别睡眠100,200,300ms ->每个执行都是20次 代码: package Homework; //1.设计一个线程类:创建3个 ...

  4. Java中常见的数据结构的区别

    把多个数据按照一定的存储方式,存储起来,称存储方式之为数据结构. 数据的存储方式有很多,数组,队列,链表,栈,哈希表等等. 不同的数据结构,性能是不一样的,比如有的插入比较快,查询比较快,但是删除比较 ...

  5. POI使用:解析xls/xlsx文件(兼容office2003/2007/2010版本)

    package cn.eguid; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; imp ...

  6. node.js零基础详细教程(6):mongodb数据库操作

    第六章 建议学习时间4小时  课程共10章 学习方式:详细阅读,并手动实现相关代码 学习目标:此教程将教会大家 安装Node.搭建服务器.express.mysql.mongodb.编写后台业务逻辑. ...

  7. 非滤波单目视觉slam笔记1

    非滤波单目视觉slam 主要分为以下8部分 数据类型 数据关联 初始化 位姿估计 地图维护 地图生成 失效恢复 回环检测 数据类型 直接法(稠密,半稠密) 基本原理是亮度一致性约束,\(J(x,y) ...

  8. docker 汇总

    整理中 ... 一. docker 基础 配置参数最佳实践 二. docker 编排工具 docker swarm mesos kubernetes AWS ECS 三. docker 生态 dock ...

  9. Linux命令 文件压缩及压缩命令

    gzip [功能说明] 文件的压缩 #gizp属于GNU软件,总性能不错,是Linux系统首选的压缩工具,tar归档命令的-z参数也是利用gzip/gunzip来解压缩 [语法格式] Gip[选项][ ...

  10. Qt WebEngine版本要求

    WebEngine是Qt5.4之后加入的新特性,用Qt WebEngine取代之前的Qt Webkit http://wiki.qt.io/QtWebEngine windows版本 windows版 ...