EventBus 事件總線之個人理解

用例:假設公司發佈了一個公告 須要經過短信 和 郵件分別2種方式 通知員工
安全

 

1:首先咱們創建領域模型微信

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

    public interface ICore
    {

    }

2:消息模型ide

 /// <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服務 分別用來發送短信和Emailui

    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對象 this

 而且全部發送消息的方法都是被動的接收這個對象 , 這樣咱們就能夠把2者的關係理解成 消息源是發佈者,編碼

具體處理消息發送的是訂閱者,從而咱們換一個思路去改造以前的方法spa

 

 public interface IEventHandler<T> where T : Entity.Core
    {
        /// <summary>
        /// 訂閱對象的具體實現
        /// </summary>
        /// <param name="entity"></param>
        void Handler(T entity);
    }

定義一個泛型接口,全部訂閱者必須實現這個接口.net

 

    /// <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出來,線程

作到能像看電視同樣,只要你打開電視就能收看到傳輸過來的畫面code

定義一個消息總線的接口 用來管理 訂閱者的註冊 以及消息的發佈

  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);
                });
            }
        }
上面的全部代碼基本上就完成了一個簡單的事件總線驅動的模式,
試試最終的運行效果。

是否是感受很神奇,這就是單純本身理解的事件總線模式。
相關文章
相關標籤/搜索