在C#中,我們可以在一個類中定義自己的事件,而其他的類可以訂閱該事件,當某些事情發生時,可以通知到該類。這對於桌面應用或者獨立的windows服務來說是非常有用的。但對於一個web應用來說是有點問題的,因為對象都是在web請求中創建的,而且這些對象生命周期都很短,因而注冊某些類的事件是很困難的。此外,注冊其他類的事件會使得類緊耦合。事件總線便可以用來解耦並重復利用應用中的邏輯。
事件總線帶來的好處和引入的問題
好處比較明顯,就是獨立出一個發布訂閱模塊
,調用者可以通過使用這個模塊,屏蔽一些線程切換問題,簡單地實現發布訂閱功能。
壞處可能比較隱晦,但這些需要足夠引起我們的重視
- 大量的濫用,將導致邏輯的分散,出現問題后很難定位。
- 沒辦法實現強類型,在編譯的時候就發現問題。
- 代碼可讀性有些問題,IDE無法識別這些協議,對IDE不友好。
EventBus
來實現,否則還是推薦自己在模塊內部實現
觀察者模式。
示例代碼
所以今天介紹一個簡單的事件總線,它是事件發布訂閱模式的實現,讓我們能在領域驅動設計(DDD)中以事件的弱引用本質對我們的模塊和領域邊界很好的解耦設計。
目前,所有的源代碼已經提交到github 上,地址:https://github.com/weizhong1988/Weiz.EventBus
程序目錄結構如下:
事件總線
事件總線是被所有觸發並處理事件的其他類共享的單例對象。要使用事件總線,首先應該獲得它的一個引用。下面有兩種方法來處理:
訂閱事件
觸發事件之前,應該先要定義該事件。EventBus為我們提供了Subscribe 方法來訂閱事件:
public void Subscribe<TEvent>(IEventHandler<TEvent> eventHandler) where TEvent : IEvent { //同步鎖 lock (_syncObject) { //獲取領域模型的類型 var eventType = typeof(TEvent); //如果此領域類型在事件總線中已注冊過 if (_dicEventHandler.ContainsKey(eventType)) { var handlers = _dicEventHandler[eventType]; if (handlers != null) { handlers.Add(eventHandler); } else { handlers = new List<object> { eventHandler }; } } else { _dicEventHandler.Add(eventType, new List<object> { eventHandler }); } } }
所以的事件都集成自IEvent,該類包含了類處理事件需要的屬性。
var sendEmailHandler = new UserAddedEventHandlerSendEmail(); var sendMessageHandler = new UserAddedEventHandlerSendMessage(); var sendRedbagsHandler = new UserAddedEventHandlerSendRedbags(); Weiz.EventBus.Core.EventBus.Instance.Subscribe(sendEmailHandler); Weiz.EventBus.Core.EventBus.Instance.Subscribe(sendMessageHandler); //Weiz.EventBus.Core.EventBus.Instance.Subscribe<UserGeneratorEvent>(sendRedbagsHandler); Weiz.EventBus.Core.EventBus.Instance.Subscribe<OrderGeneratorEvent>(sendRedbagsHandler);
發布事件
對於事件源,則可以通過Publish 方法發布事件。觸發一個事件很簡單,如下所示:
public void Publish<TEvent>(TEvent tEvent, Action<TEvent, bool, Exception> callback) where TEvent : IEvent { var eventType = typeof(TEvent); if (_dicEventHandler.ContainsKey(eventType) && _dicEventHandler[eventType] != null && _dicEventHandler[eventType].Count > 0) { var handlers = _dicEventHandler[eventType]; try { foreach (var handler in handlers) { var eventHandler = handler as IEventHandler<TEvent>; eventHandler.Handle(tEvent); callback(tEvent, true, null); } } catch (Exception ex) { callback(tEvent, false, ex); } } else { callback(tEvent, false, null); } }
下面是發布事件的調用:
var orderGeneratorEvent = new OrderGeneratorEvent { OrderId = Guid.NewGuid() }; System.Console.WriteLine("{0}下單成功", orderGeneratorEvent.OrderId); Weiz.EventBus.Core.EventBus.Instance.Publish(orderGeneratorEvent, CallBack);
定義處理事件
要處理一個事件,應該要實現IEventHandler接口,如下所示:
/// <summary> /// send email /// </summary> public class UserAddedEventHandlerSendEmail : IEventHandler<UserGeneratorEvent> { public void Handle(UserGeneratorEvent tEvent) { System.Console.WriteLine(string.Format("{0}的郵件已發送", tEvent.UserId)); } }
處理多事件
在一個單一的處理句柄中,可以處理多個事件。這時,你應該為每個事件實現IEventHandler。比如:
/// <summary> /// red bags. /// </summary> public class UserAddedEventHandlerSendRedbags : IEventHandler<UserGeneratorEvent>,IEventHandler<OrderGeneratorEvent> { public void Handle(OrderGeneratorEvent tEvent) { System.Console.WriteLine(string.Format("{0}的下單紅包已發送", tEvent.OrderId)); } public void Handle(UserGeneratorEvent tEvent) { System.Console.WriteLine(string.Format("{0}的注冊紅包已發送", tEvent.UserId)); } }
最后
以上,就把事件總線介紹完了,完整的代碼,請到github 上下載,這個只是EventBus 的簡單實現,各位可以根據自己的實際場景和需求,優化修改。