В прошлой части я наметил основу будущего диспетчера событий. Пора заняться реализацией.
Для начала определим стоящие перед нами проблемы. К счастью их всего две:
Теперь можно перейти к реализации 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
Комментариев нет:
Отправить комментарий