Compartilhar via


Práticas recomendadas para o padrão de design do observador

No .NET, o padrão de design de observador é implementado como um conjunto de interfaces. A interface System.IObservable<T> representa o provedor de dados, que também é responsável por fornecer uma implementação IDisposable que permite que os observadores cancelem a assinatura de notificações. A interface System.IObserver<T> representa o observador. Este tópico descreve as práticas recomendadas que os desenvolvedores devem seguir ao implementar o padrão de design de observador usando essas interfaces.

Threading

Normalmente, um provedor implementa o método IObservable<T>.Subscribe adicionando um observador específico a uma lista de assinantes que é representada por algum objeto de coleção e implementa o método IDisposable.Dispose removendo um determinado observador da lista de assinantes. Um observador pode chamar esses métodos a qualquer momento. Além disso, como o contrato de provedor/observador não especifica quem é responsável por cancelar a assinatura após o método de retorno de chamada IObserver<T>.OnCompleted, o provedor e o observador podem tentar ambos remover o mesmo membro da lista. Devido a essa possibilidade, tanto o método Subscribe quanto o método Dispose devem ser thread-safe. Normalmente, isso envolve o uso de uma coleção simultânea ou um bloqueio. As implementações que não são thread-safe devem documentar explicitamente esse fato.

Quaisquer garantias adicionais devem ser especificadas em uma camada no início do contrato de provedor/observador. Os implementadores devem chamar claramente ao imporem requisitos adicionais para evitar confusão do usuário sobre o contrato do observador.

Tratando exceções

Devido ao fraco acoplamento entre um provedor de dados e um observador, as exceções no padrão de design do observador devem ser informativas. Isso afeta como os provedores e observadores manipulam exceções no padrão de design do observador.

O Provedor - Chamando o Método OnError

O método OnError serve como uma mensagem informativa para observadores, da mesma forma que o método IObserver<T>.OnNext. No entanto, o método OnNext foi projetado para fornecer a um observador dados atuais ou atualizados, enquanto o método OnError foi projetado para indicar que o provedor não é capaz de fornecer dados válidos.

O provedor deve seguir essas práticas recomendadas ao manipular exceções e chamar o método OnError:

  • O provedor deverá manipular suas próprias exceções se houver algum requisito específico.

  • O provedor não deve esperar ou exigir que os observadores manipulem exceções de alguma maneira específica.

  • O provedor deve chamar o método OnError ao manipular uma exceção que comprometa sua capacidade de fornecer atualizações. Informações sobre essas exceções podem ser passadas para o observador. Em outros casos, não há necessidade de notificar os observadores com relação a uma exceção.

Uma vez que o provedor chame o método OnError ou IObserver<T>.OnCompleted, não deverá haver nenhuma notificação adicional e o provedor poderá cancelar a assinatura de seus observadores. No entanto, os observadores podem também cancelar eles próprios sua assinatura a qualquer momento, inclusive antes e depois de receberem uma notificação OnError ou IObserver<T>.OnCompleted. O padrão de design do observador não determina se o provedor ou o observador é responsável pelo cancelamento da assinatura; portanto, é possível que ambos tentem cancelar a assinatura. Normalmente, quando os observadores cancelam a assinatura, eles são removidos de uma coleção de assinantes. Em um aplicativo de thread único, a implementação IDisposable.Dispose deve garantir que uma referência de objeto seja válida e que o objeto seja um membro da coleção de assinantes antes de tentar removê-lo. Em um aplicativo com multithread, deve-se usar um objeto de coleção thread-safe, tal como um objeto System.Collections.Concurrent.BlockingCollection<T>.

O Observador — Implementando o Método OnError

Quando um observador recebe uma notificação de erro de um provedor, o observador deve tratar a exceção como informativa e não deve ser necessária qualquer ação específica.

O observador deve seguir essas práticas recomendadas ao responder a uma chamada de método OnError de um provedor:

  • O observador não deve lançar exceções de suas implementações de interface, tais como OnNext ou OnError. No entanto, se o observador lançar exceções, ele deverá esperar que essas exceções fiquem sem tratamento.

  • Para preservar a pilha de chamadas, um observador que deseja gerar um objeto Exception, que foi passado para o seu método OnError, deveria encapsular a exceção antes de lançá-la. Um objeto de exceção padrão deve ser usado para essa finalidade.

Práticas recomendadas adicionais

A tentativa de cancelar registro no método IObservable<T>.Subscribe pode resultar em uma referência nula. Portanto, é recomendável que você evite essa prática.

Embora seja possível anexar um observador para vários provedores, o padrão recomendado é anexar uma IObserver<T> instância a uma única instância IObservable<T>.

Confira também