天天看點

EventAggregator, EventBus的實作

系列主題:基于消息的軟體架構模型演變

.net中事件模型很優雅的實作了觀察者模式,同時被大量的使用在各種架構中。如果我們非要給事件模型挑毛病,我覺得有兩點:

  • 實作起來略微繁瑣
  • 正如我們上篇文章分析,事件模型在特定的情況下會發生記憶體洩漏

于是我們想到了更加簡單易用的模型:EventAggregator,正如該名稱所描述,EventAggregator将觀察者都聚合在一個容器裡,向EventAggregator釋出一個主題,EventAggregator會找到對該主題感興趣的觀察者并通知他。

EventAggregator, EventBus的實作

Prism架構中實作了一個典型的EventAggregator,有時候我們又把此類實作叫做EventBus。

MVVM Light中實作了一個叫Messenger的bus,Messenger是EventBus、EventAggregator等概念的抽象,因為此時的主題已經表現的像是消息一樣,是以Messenger這一名稱也更加靠近基于消息架構這一主題。

一、實作一個簡單的EventBus

EventBus的職責有兩點:

  • 注冊觀察者
  • 釋出主題

是以接口定義為:

public interface ISimpleEventBus
    {
        void Register<TEvent>(Action<TEvent> action);
        void Publish<TEvent>(TEvent @event);
    }
      

所有的主題都可以用Action<TEvent> action來表示,實作起來也很簡單:

public class SimpleEventBus
    {
        public static ConcurrentDictionary<Type,List<Action<object>>> Dictionary=new ConcurrentDictionary<Type, List<Action<object>>>();
 
        public void Register<TEvent>(Action<TEvent> action)
        {
            List<Action<object>> actionList;
            if (!Dictionary.TryGetValue(typeof (TEvent), out actionList))
            {
                actionList=new List<Action<object>>();
                Dictionary[typeof (TEvent)] = actionList;
            }
            actionList.Add(o=>action((TEvent)o));
        }

        public void Publish<TEvent>(TEvent @event)
        {
            List<Action<object>> actionList;
            if (Dictionary.TryGetValue(typeof (TEvent), out actionList))
            {
                foreach (var action in actionList)
                {
                    action(@event);
                }
            }
        }
    }
      

EventBus内部通過一個類型為ConcurrentDictionary<Type,List<Action<object>>> 的字典來存儲主題和觀察者清單。寫個測試試試:

[Test]
        public void Should_handle_registered_action()
        {
            var eventBus = new SimpleEventBus();

            var number = 0;
            eventBus.Register<MessageA>(m=>number=m.Number);
            eventBus.Publish(new MessageA(2));

            number.Should().Be(2);
        }

        internal class MessageA
        {
            public MessageA(int number)
            {
                Number = number;
            }
            public int Number { get; private set; }
        }
      

我們自己寫的這個simpleEventBus已經能夠應付大部分情況了,使用起來也比事件模型簡單很多。但是仍然沒有解決記憶體洩漏的問題。

二、MVVM Light中Messenger的實作

Messenger的實作是我們這個簡單eventBus的更新版,首先他抽象了概念,從事件到消息是思維的一個轉變,Messenger認為所有注冊的主題都是消息。其次采用WeakReference來關聯觀察者和主題。

public void Register<TMessage>(object recipient, Action<TMessage> action)
        {
            lock (_registerLock)
            {
                var messageType = typeof(TMessage);

                Dictionary<Type, List<WeakAction>> recipients;
               
                    if (_recipientsStrictAction == null)
                    {
                        _recipientsStrictAction = new Dictionary<Type, List<WeakAction>>();
                    }

                    recipients = _recipientsStrictAction;

                lock (recipients)
                {
                    List<WeakAction> list;

                    if (!recipients.ContainsKey(messageType))
                    {
                        list = new List<WeakAction>();
                        recipients.Add(messageType, list);
                    }
                    else
                    {
                        list = recipients[messageType];
                    }

                    var weakAction = new WeakAction<TMessage>(recipient, action);
                   
                    list.Add(weakAction);
                }
            }

            RequestCleanup();
        }
      

這個實作跟我們實作的EventBus大同小異,不同之處是dictionary類型為Dictionary<Type, List<WeakAction>>。

WeakAction的構造函數:

public WeakAction(object target, Action<T> action)
        {

            if (action.Method.IsStatic)
            {
                _staticAction = action;

                if (target != null)
                {
                    Reference = new WeakReference(target);
                }

                return;
            }


            Method = action.Method;
            ActionReference = new WeakReference(action.Target);
            Reference = new WeakReference(target);
        }      

ActionReference = new WeakReference(action.Target); 這句話将一個object包裝進了WeakReference。

發送消息的代碼也跟我們自己實作的EventBus大同小異,大家可以直接看代碼對比。

寫一個測試看看如何使用Messenger:

[Test]
        public void Should_handle_registered_actions()
        {
            int number = 0;
            Messenger.Default.Register<MessageA>(this,m=>number=m.Number);
            Messenger.Default.Send(new MessageA(2));

            number.Should().Be(2);
        }

        internal class MessageA
        {
            public MessageA(int number)
            {
                Number = number;
            }
            public int Number { get; private set; }
        }      

我們注意到Messenger采用了消息的概念,是以釋出主題也将方法名從publish改為了send。一般我們都說釋出一個事件,發送一個消息。Messenger所提到的概念已經快要接近ServiceBus了。

作者:Richie Zhang

來源:http://www.cnblogs.com/richieyang/

聲明:本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。