В прошлой части я наметил основу будущего диспетчера событий. Пора заняться реализацией.
Для начала определим стоящие перед нами проблемы. К счастью их всего две:
Теперь можно перейти к реализации IPublishWay. В простейшем случае можно сделать так:
Остался последний штрих - реализовать IListenerSource. Для первой реализации можно взять обыкновенный список. Но лучше воспользоваться новой коллекцией из .net 4 - ISet. Она не позволяет дважды добавить один и тот же объект, что избавит от необходимости предварительной проверки.
На этом простая реализация диспетчера сообщений завершена, можно конфигурировать и использовать. Но это еще не все... В следующей части рассмотрим вопросы тестирования.
Код проекта по-прежнему можно найти на github
Для начала определим стоящие перед нами проблемы. К счастью их всего две:
- Получение списка подписчиков сообщений.
- Отправка сообщений подписчикам.
public interface IListenerSource
{
IEnumerable<IListener<TMessage>> ResolveListenersFor<TMessage>();
}
public interface IPublishWay
{
void Publish<TMessage>(TMessage message, IListener<TMessage> listener);
}Эти интерфейсы открывают путь к простейшей реализации диспетчера событий: public class Publisher : IPublisher
{
protected readonly IListenerSource ListenerSource;
protected readonly IPublishWay PublishWay;
public Publisher(IListenerSource listenerSource, IPublishWay publishWay)
{
if(listenerSource == null)
throw new ArgumentNullException("listenerSource");
if(publishWay == null)
throw new ArgumentNullException("publishWay");
ListenerSource = listenerSource;
PublishWay = publishWay;
}
public virtual void Publish<TMessage>(TMessage message)
{
foreach (var listener in ListenerSource.ResolveListenersFor<TMessage>())
{
PublishWay.Publish(message, listener);
}
}
}
Теперь можно перейти к реализации IPublishWay. В простейшем случае можно сделать так:
public class SimplePublishWay : IPublishWay
{
public virtual void Publish<TMessage>(TMessage message, IListener<TMessage> listener)
{
listener.ListenTo(message);
}
}Для работы в реальных условиях такая реализация вряд ли годится. Но пригодится для тестировочных конфигураций Publisher'a. Но об этом в следующей части. А сейчас посмотрим на более полезную реализацию, с использованием SynchronizationContext. От просто SynchronizationContext пользы немного, но при использовании его наследника WindowsFormsSynchronizationContext мы получим маршаллинг событий в поток пользовательского интерфейса. Подробнее об это можно почитать например здесь. Опять же, простейшая реализация: public class SynchronizationContextPublishWay : IPublishWay
{
private readonly SynchronizationContext _synchronizationContext;
public SynchronizationContextPublishWay(SynchronizationContext synchronizationContext)
{
_synchronizationContext = synchronizationContext;
}
public void Publish<TMessage>(TMessage message, IListener<TMessage> listener)
{
_synchronizationContext.Post(x => listener.ListenTo(message), null);
}
}Метод Post позволит реализовать асинхронную передачу сообщений, освободив вызывающий поток для другой работы. Остался последний штрих - реализовать IListenerSource. Для первой реализации можно взять обыкновенный список. Но лучше воспользоваться новой коллекцией из .net 4 - ISet. Она не позволяет дважды добавить один и тот же объект, что избавит от необходимости предварительной проверки.
public class SimpleAssignee : IAssignee
{
private readonly ISet<object> _listeners;
private readonly object _latch;
public SimpleAssignee()
{
_listeners = new HashSet<object>();
_latch = new object();
}
public virtual void Subscribe(object listener)
{
lock(_latch)
{
_listeners.Add(listener);
}
}
public virtual void Unsubscribe(object listener)
{
lock(_latch)
{
_listeners.Remove(listener);
)
}
public virtual IEnumerable<IListener<TMessage>> ResolveListenersFor<TMessage>()
{
lock(_latch)
{
return _listeners.OfType<IListener<TMessage>>().ToList();
}
}
}
На этом простая реализация диспетчера сообщений завершена, можно конфигурировать и использовать. Но это еще не все... В следующей части рассмотрим вопросы тестирования.
Код проекта по-прежнему можно найти на github
Комментариев нет:
Отправить комментарий