Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
No .NET, o padrão de design do observador é implementado como um conjunto de interfaces. A System.IObservable<T> interface representa o provedor de dados, que também é responsável por fornecer uma IDisposable implementação que permite aos observadores cancelar a assinatura de notificações. A System.IObserver<T> interface representa o observador.
Este artigo descreve as práticas recomendadas a seguir quando você implementa o padrão de design do observador com essas interfaces.
Considere alternativas antes de implementar
As interfaces IObservable<T> e IObserver<T> são adequadas para cenários de notificação baseadas em push, mas outros padrões de .NET podem ser mais adequados. Para uma notificação simples em um único aplicativo, use eventos. Para sequências assíncronas do tipo pull, nas quais o consumidor controla o ritmo, use IAsyncEnumerable<T>. Para padrões de produtor-consumidor com backpressure, use System.Threading.Channels. Para composição, filtragem e transformação de eventos complexos, use o pacote System.Reactive (Rx.NET) em vez de implementar IObservable<T> diretamente. Para obter mais informações, consulte o padrão de design do Observador.
Usar um tipo separado para dados de notificação
O objeto que contém os dados que o provedor envia para seus observadores corresponde ao parâmetro de tipo genérico de IObservable<T> e IObserver<T>. Embora esse objeto possa ser o mesmo que a implementação, defina-o IObservable<T> como um tipo separado. Um tipo de dados dedicado mantém as responsabilidades do provedor separadas do conteúdo de notificação e facilita a evolução da API.
Não confie na ordem de notificação
A ordem na qual os observadores recebem notificações não está definida. O provedor pode usar qualquer método para determinar a ordem, então não escreva observadores que dependam de ser notificados antes ou depois de outro observador.
Tornar Subscribe e Dispose seguros para threads
Normalmente, um provedor implementa o IObservable<T>.Subscribe método adicionando um observador a uma lista de assinantes representada por um objeto de coleção e implementa o IDisposable.Dispose método removendo o observador dessa lista. Um observador pode chamar esses métodos a qualquer momento. O contrato entre provedor e observador não especifica quem é responsável por cancelar a assinatura após o método de retorno de chamada IObserver<T>.OnCompleted, portanto, o provedor e o observador podem tentar remover o mesmo membro da lista.
Para evitar condições de corrida, torne ambos os métodos Subscribe e Dispose seguros para threads. 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 que não são.
Documentar quaisquer garantias de contrato extra
Especifique quaisquer garantias extras em uma camada sobre o contrato de provedor/observador. Quando você impõe outros requisitos, chame-os claramente para que os usuários não fiquem confusos sobre o contrato de observador.
Tratar exceções como informativas
Devido ao acoplamento flexível entre um provedor de dados e um observador, as exceções no padrão de design do observador devem ser informativas. Essa característica afeta como provedores e observadores lidam com exceções.
Chamar OnError somente quando as atualizações não puderem continuar
O OnError método destina-se como uma mensagem informativa aos observadores, assim como o IObserver<T>.OnNext método. No entanto, o OnNext método fornece a um observador dados atuais ou atualizados, enquanto o OnError método indica que o provedor não pode fornecer dados válidos.
Siga estas práticas recomendadas ao lidar com exceções e chamar o OnError método:
- O provedor deve lidar com suas próprias exceções se tiver requisitos específicos.
- O provedor não deve esperar ou exigir que os observadores lidem com exceções de qualquer maneira específica.
- O provedor deve chamar o OnError método quando trata de uma exceção que compromete sua capacidade de fornecer atualizações. Passe informações sobre essas exceções para o observador. Em outros casos, não é necessário notificar observadores de uma exceção.
Depois que o provedor chamar o método OnError ou IObserver<T>.OnCompleted, não deverá haver mais notificações, e o provedor poderá cancelar a inscrição de seus observadores. No entanto, os observadores também podem cancelar a assinatura a qualquer momento, inclusive antes e depois de receber 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 por cancelar a assinatura, portanto, ambos podem tentar 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 única, a implementação IDisposable.Dispose deve garantir que uma referência de objeto seja válida e que o objeto faça parte da coleção de assinantes antes de tentar remover o objeto. Em um aplicativo multithreaded, use um bloqueio para proteger a coleção de observadores.
Tratar as notificações do OnError como informativas em observadores
Quando um observador recebe uma notificação de erro de um provedor, ele deve tratar a exceção apenas como informativa e não deve ser obrigado a tomar nenhuma ação específica.
Siga estas práticas recomendadas ao responder a uma chamada de método OnError vinda de um provedor:
- Não gere exceções de implementações de interface como OnNext ou OnError. Se o observador gerar exceções, espere que essas exceções não sejam tratadas.
- Para preservar a pilha de chamadas, um observador que deseja lançar um objeto Exception que foi passado ao seu método OnError deve encapsular a exceção antes de lançá-la. Use um objeto de exceção padrão para essa finalidade.
Não cancelar o registro no método Subscribe
Não tente cancelar o registro no IObservable<T>.Subscribe método, pois ele pode resultar em uma referência nula.
Anexar um observador a um único provedor
Embora você possa anexar um observador a vários provedores, o padrão recomendado é anexar uma IObserver<T> instância a apenas uma IObservable<T> instância.