Рекомендации по шаблону разработки Observer

В .NET конструктивный шаблон наблюдателя реализован в виде набора интерфейсов. Интерфейс System.IObservable<T> представляет поставщик данных, который также отвечает за предоставление реализации IDisposable, позволяющей наблюдателям отменять подписку на уведомления. Интерфейс System.IObserver<T> представляет наблюдателя. В этом разделе содержатся рекомендации, которым должны следовать разработчики при реализации шаблона разработки наблюдателя с помощью этих интерфейсов.

Потоки

Как правило, поставщик реализует метод IObservable<T>.Subscribe путем добавления определенного наблюдателя в список подписчиков, представленный неким объектом коллекции, и он реализует метод IDisposable.Dispose путем удаления определенного наблюдателя из списка подписчиков. Наблюдатель может вызвать эти методы в любое время. Кроме того, поскольку в контракте поставщика и наблюдателя не указано, кто несет ответственность за отмену подписки после реализации метода обратного вызова IObserver<T>.OnCompleted, попытаться удалить тот же самый элемент из списка может как поставщик, так и наблюдатель. В связи с этим методы Subscribe и Dispose должны быть потокобезопасными. Как правило, в этом случае предполагается использование параллельной коллекции или блокировки. Реализации, которые не являются потокобезопасными, должны явным образом сообщать об этом.

Любые дополнительные гарантии должны быть указаны на уровне выше контракта поставщика и наблюдателя. Чтобы избежать недоразумений, связанных с контрактом наблюдателя, разработчики должны четко сообщать о введении дополнительных требований.

Обработка исключений

Из-за слабой связи между поставщиком данных и наблюдателем исключения в шаблоне разработки наблюдателя носят информативный характер. Это оказывает влияние на процесс обработки исключений поставщиками и наблюдателями в шаблоне разработки наблюдателя.

Поставщик— вызов метода OnError

Метод OnError предназначен для использования в качестве информационного сообщения для наблюдателей и очень похож на метод IObserver<T>.OnNext. Однако метод OnNext позволяет предоставлять наблюдателю текущие или обновленные данных, тогда как метод OnError указывает на то, что поставщик не может предоставлять допустимые денные.

При обработке исключений и вызове метода OnError поставщику следует придерживаться приведенных ниже рекомендаций.

  • Поставщик должен обрабатывать свои собственные исключения при наличии конкретных требований.

  • Поставщик не должен ожидать или требовать, чтобы наблюдатели обрабатывали исключения каким-либо определенным образом.

  • При обработке исключения, подвергающего риску его возможности по предоставлению обновлений, поставщику следует вызывать метод OnError. Информацию по таким исключениям можно передать наблюдателю. В других случаях уведомлять наблюдателей об исключении не нужно.

После того, как поставщик вызовет метод OnError или IObserver<T>.OnCompleted, дальнейшие уведомления поступать не должны, и поставщик может отменить подписку для своих наблюдателей. Однако наблюдатели также могут отменить свою подписку в любой момент — до или после получения уведомления OnError или IObserver<T>.OnCompleted. В шаблоне разработки наблюдателя не указывается, кто несет ответственность за отмену подписки. Таким образом, есть вероятность, что отменить подписку попытается и поставщик, и наблюдатель. Обычно наблюдатель, отменяющий подписку, удаляется из коллекции подписчиков. В однопоточном приложении перед удалением объекта реализация IDisposable.Dispose должна гарантировать, что ссылка на объект является действительной и объект входит в коллекцию подписчиков. В многопоточном приложении следует использовать объект потокобезопасной коллекций, такой как System.Collections.Concurrent.BlockingCollection<T>.

Наблюдатель — реализация метода OnError

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

При ответе на вызов метода OnError от поставщика наблюдателю следует придерживаться приведенных ниже рекомендаций.

  • Наблюдатель не должен вызывать исключения из своих реализаций интерфейса, таких как OnNext или OnError. Однако если наблюдатель не создает исключения, следует ожидать, что исключения останутся необработанными.

  • Чтобы сохранить стек вызовов, наблюдатель, желающий создать объект Exception, который был передан в его метод OnError, должен упаковать исключение до его создания. Для этой цели можно использовать стандартный объект исключения.

Дополнительные рекомендации

Результатом попытки отмены регистрации в методе IObservable<T>.Subscribe может быть пустая ссылка. Поэтому рекомендуется воздержаться от таких действий.

Несмотря на наличие возможности прикрепить наблюдателя к нескольким поставщикам, рекомендуется присоединить экземпляр IObserver<T> только к одному экземпляру IObservable<T>.

См. также