• EventAggregator, EventBus的实现


    系列主题:基于消息的软件架构模型演变

    .net中事件模型很优雅的实现了观察者模式,同时被大量的使用在各种框架中。如果我们非要给事件模型挑毛病,我觉得有两点:

    • 实现起来略微繁琐
    • 正如我们上篇文章分析,事件模型在特定的情况下会发生内存泄漏

    于是我们想到了更加简单易用的模型:EventAggregator,正如该名称所描述,EventAggregator将观察者都聚合在一个容器里,向EventAggregator发布一个主题,EventAggregator会找到对该主题感兴趣的观察者并通知他。

    IC245765

    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了。

  • 相关阅读:
    SCAU 9504 面试
    SCAU 9503 懒人选座位
    SCAU 8628 相亲
    SCAU 10691 ACM 光环
    SCAU 8626 原子量计数
    SCAU 10674 等差对
    HDU ACM 1048 The Hardest Problem Ever (水题)
    SCAU 9502 ARDF
    SCAU 10686 DeathGod不知道的事情
    SCAU 8629 热身游戏(高精度)
  • 原文地址:https://www.cnblogs.com/richieyang/p/4909397.html
Copyright © 2020-2023  润新知