Using Event Aggregator Pattern, a message hub made simple



Tenho usado o Event Aggregator em UI para sincronização e comunicação indirecta entre user controls/views e entre controllers/presenters. Mas pode ser usado em variadas situações.

Ao introduzir um agregador de eventos entre publishers e subscribers, consegue-se baixo acoplamento. O subscriber observa o agregador de eventos em vez do publisher e o publisher por sua vez apenas sabe acerca do agregador de eventos e não sabe nada sobre os subscribers.



Um agregador simples
public sealed class EventAggregator : IEventAggregator
    {
        private readonly IDictionary<Type, IList> _subscriptions = new Dictionary<Type, IList>();

        #region Singleton - not quite as lazy, but thread-safe without using locks

        private static readonly EventAggregator instance = new EventAggregator();

        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static EventAggregator()
        {
        }

        private EventAggregator()
        {
        }

        public static EventAggregator Instance
        {
            get
            {
                return instance;
            }
        }

        #endregion

        public void Publish<TMessage>(TMessage message) where TMessage : IMessage
        {
            if (message == null) throw new ArgumentNullException("no message");

            Type messageType = typeof(TMessage);

            if (_subscriptions.ContainsKey(messageType))
            {
                var subscriptionList = new List<ISubscription<TMessage>>(
                    _subscriptions[messageType].Cast<ISubscription<TMessage>>());

                foreach (var subscription in subscriptionList)
                {
                    subscription.Action(message);
                }
            }
        }

        public void UnSubscribe<TMessage>(ISubscription<TMessage> subscription) where TMessage : IMessage
        {
            Type messageType = typeof(TMessage);

            if (_subscriptions.ContainsKey(messageType))
            {
                _subscriptions[messageType].Remove(subscription);
            }
        }

        public ISubscription<TMessage> Subscribe<TMessage>(Action<TMessage> action) where TMessage : IMessage
        {
            Type messageType = typeof(TMessage);
            var subscription = new Subscription<TMessage>(this, action);

            if (_subscriptions.ContainsKey(messageType))
            {
                _subscriptions[messageType].Add(subscription);
            }
            else
            {
                _subscriptions.Add(messageType, new List<ISubscription<TMessage>> { subscription });
            }

            return subscription;
        }

        public void ClearAllSubscriptions()
        {
            ClearAllSubscriptions(null);
        }

        public void ClearAllSubscriptions(Type[] exceptMessages)
        {
            foreach (var messageSubscriptions in new Dictionary<Type, IList>(_subscriptions))
            {
                bool canDelete = true;
                if (exceptMessages != null)
                {
                    canDelete = !exceptMessages.Contains(messageSubscriptions.Key);
                }

                if (canDelete)
                {
                    _subscriptions.Remove(messageSubscriptions);
                }
            }
        }
        
    }

A subscrição
public class Subscription<TMessage> : ISubscription<TMessage> where TMessage : IMessage
    {
        public Action<TMessage> Action { get; private set; }
        public IEventAggregator EventAggregator { get; private set; }

        public Subscription(IEventAggregator eventAggregator, Action<TMessage> action)
        {
            if (eventAggregator == null) throw new ArgumentNullException("no eventAggregator");
            if (action == null) throw new ArgumentNullException("no action");

            EventAggregator = eventAggregator;
            Action = action;
        }


        #region IDisposable Support
        private bool disposedValue = false; // To detect redundant calls

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    EventAggregator.UnSubscribe(this);
                }

                disposedValue = true;
            }
                
        }

        // This code added to correctly implement the disposable pattern.
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion
    }

Wrapper do agregador
public static class Messages
    {

        public static IEventAggregator Hub
        {
            get
            {
                return Service.EventAggregator.Instance;
            }

        }

    }

Exemplo de Invocação
private void ShowItemName(IEventAggregator messageHub)
        {
            messageHub.Subscribe<Item>(item => { /* ... */; });
        }

private static void SetItemName(IEventAggregator messageHub, TextBox textBox)
        {
           messageHub.Publish(new Item { /* ... */ });
        }

[...]

ShowItemName(Messages.Hub);

SetItemName(Messages.Hub, (TextBox)sender);




Implementações de Event Aggregator:






Referências: Simple message-based Event Aggregator, Implementing the Singleton Pattern in C#, Why Use an Event Aggregator?
Licença CC BY-SA 4.0 Silvia Pinhão Lopes, 1.10.17
Print Friendly and PDF

Sem comentários:

Com tecnologia do Blogger.