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:
- Easy Event Aggregator por Nima Ara
- Asynchronous Event Aggregator por Timothy Makarov
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
Sem comentários: