Страницы

четверг, 25 августа 2011 г.

Реализуем EventPublisher. Шаг четвёртый. Переходим в замок Windsor.

  Изначально EventPublisher прездназначался для отправки сообщений экземплярам классов, которые уже существуют, и явным образом подписаны. Но часто полезно, когда получатель сообщения не живёт постоянно, а создаваётся во время его отправки, и время жизни ограничено временем обработки сообщения. Такой вот одноразовый обработчик: создался, обработал, испарился. Такую вещь можно реализовать как рефлексивный IListenerSource. Но этот способ не лишён недостатков, главный из которых - сложности с управлением зависимостями.

  К счастью, в этом мире проблема управления зависимостями уже решена с помощью DI контейнеров.  Осталось только выбрать контейнер, и написать основанный на контейнере IListenerSource. Не буду скрывать, мой любимый контейнер - Castle.Windsor. Его и буду использовать.

  Перед написанием собственно IListenerSource, стоит подумать об еще одной вещи: как регистрировать наших подписчиков в контейнере? Windsor широко использует принцип convention over configuration, и имеет множество встроенных средств для регистрации компонентов по соглашением. Значит надо такое соглашение придумать. Вот оно:
public interface ITransientListener<TMessage> : IListener<TMessage>
{
} 
  Как можно догадаться из названия - регистрировать подписчиков будем с Transient Lifestyle, чтобы обеспечить желаемый стиль работы: создался, обработал, испарился.

  Следующий логичный шаг - это регистрация обработчиков событий в контейнере. Конечно, в каждом проекте это придётся делать по-новому, но было бы неплохо написать помощника в этом нудном деле. Я не буду приодить его здесь, дабы не загромождать блог кодом, но можно посмотреть на github

  Теперь у нас есть всё для создания IListenersSource: соглашение о подписчиках и инструмент их регистрации. С Windsor'ом это совсем не сложно:
public class TransientSource : IListenerSource
{
	private readonly IKernel _kernel;

	public TransientSource(IKernel kernel)
	{
		_kernel = kernel;
	}

	public virtual IEnumerable<IListener<TMessage>> ResolveListenersFor<TMessage>()
	{
		return _kernel.ResolveAll<ITransientListener<TMessage>>();
	}
}
  Теперь мы можем сконфигурировать EventPublisher, чтобы он перенаправлял сообщения одноразовым обработчикам из контейнера. Но как же быть со старыми добрыми долгоживущими экземплярами подписчиков? Ответ прост: нужен источник подпичиков, который бы аггрегировал подписчиков из разных источников - CompositeListenerSourсe.

  Остался последний штрих. Windsor предлагает еще одну концепцию для упрощения рутинных действий при регистрации компонентов: Facility. Почему бы не сделать EventPublisherFacility?
Основые действия, которые на неё возлагаюстся:
  1. Конфигурирует EventPublisher используя IPublishWay зарегистрированный в контейнере или переданный в конструкторе, и CompositeListenerSource, в который добавлены TransientListnerSource, и все зарегистрировные в контейнере IListenerSource.
  2. Регистрирует в контейнере IAssignee, для возможности добавлять подписчиков. 
  Реализация это не так уж сложно. Теперь у нас есть зарегистированный в контейнере EventPublisher  с возможностью подписывать/отписывать и вытаскивать из контейнера одноразовые обработчики.

  Надеюсь, это материал будет кому-нибудь полезен. Код проекта на github.

Комментариев нет:

Отправить комментарий