Procedimientos recomendados para modelos de diseño de observador
En .NET, el modelo de diseño de observador se implementa como un conjunto de interfaces. La interfaz System.IObservable<T> representa al proveedor de datos, que también es responsable de proporcionar una implementación IDisposable que permite a los observadores cancelar la suscripción a las notificaciones. La interfaz System.IObserver<T> representa al observador. En este tema se describen los procedimientos recomendados que los desarrolladores deben seguir al implementar el patrón de diseño de observador con estas interfaces.
Subprocesos
Normalmente, para implementar el método IObservable<T>.Subscribe, un proveedor agrega un observador determinado a una lista de suscriptores que se representa mediante un objeto de colección; por su parte, para implementar el método IDisposable.Dispose, el proveedor quita un observador determinado de la lista de suscriptores. Un observador puede llamar a estos métodos en cualquier momento. Además, dado que el contrato de proveedor/observador no especifica quién es el responsable de cancelar la suscripción después del método de devolución de llamada IObserver<T>.OnCompleted, el proveedor y el observador pueden intentar quitar el mismo miembro de la lista. Debido a esta posibilidad, ambos métodos Subscribe y Dispose deben ser seguros para subprocesos. Normalmente, esto implica el uso de una colección simultánea o un bloqueo. Las implementaciones que no son seguras para subprocesos deben documentar explícitamente que no lo son.
Las posibles garantías adicionales deben especificarse en un nivel por encima del contrato de proveedor/observador. Los implementadores deben dejar claro que imponen requisitos adicionales para evitar confusiones al usuario sobre el contrato de observador.
Controlar las excepciones
Debido al acoplamiento flexible entre un proveedor de datos y un observador, las excepciones en el patrón de diseño de observador tienen un carácter informativo. Esto afecta a cómo los proveedores y observadores controlan las excepciones en el patrón de diseño de observador.
El proveedor: Llamada al método OnError
El propósito del método OnError es servir de mensaje informativo para el observador, de modo parecido al método IObserver<T>.OnNext. Sin embargo, el método OnNext está diseñado para proporcionarle a un observador información actual o actualizada, mientras que el método OnError está diseñado para indicar que el proveedor no puede proporcionar datos válidos.
El proveedor debe seguir estas recomendaciones a la hora de controlar las excepciones y llamar al método OnError:
El proveedor debe controlar sus propias excepciones si tiene requisitos específicos.
El proveedor no debe esperar ni exigir que los observadores controlen las excepciones en ningún modo en particular.
El proveedor debe llamar al método OnError cuando controla una excepción que pone en peligro su capacidad para proporcionar actualizaciones. La información sobre este tipo de excepciones se puede pasar al observador. En otros casos, no es necesario informar a los observadores de una excepción.
Una vez que el proveedor llama al método OnError o IObserver<T>.OnCompleted, no debe haber más notificaciones y el proveedor puede cancelar la suscripción de sus observadores. No obstante, también los observadores pueden cancelar ellos mismos su suscripción en cualquier momento, incluso antes y después de recibir una notificación OnError o IObserver<T>.OnCompleted. El patrón de diseño de observador no indica si el proveedor o el observador son responsables de cancelar la suscripción; por lo tanto, es posible que ambos traten de cancelar la suscripción. Normalmente, cuando los observadores cancelan su suscripción, se quitan de una colección de los suscriptores. En una aplicación de un único subproceso, la implementación de IDisposable.Dispose debe asegurarse de la validez de una referencia de objeto y de que el objeto es miembro de la colección de suscriptores antes de intentar quitarlo. En una aplicación multiproceso, debe utilizarse un objeto de colección seguro para subprocesos, por ejemplo, un objeto System.Collections.Concurrent.BlockingCollection<T>.
El observador: implementación del método OnError
Cuando un observador recibe una notificación de error de un proveedor, debe considerar solo el valor informativo de la excepción sin que sea necesario realizar ninguna acción especial.
El observador debe seguir estas recomendaciones al responder a una llamada de método OnError de un proveedor:
El observador no debe generar excepciones desde sus implementaciones de interfaz, como OnNext o OnError. No obstante, si el observador genera excepciones, no debe esperar que se controlen.
Para conservar la pila de llamadas, un observador que desee producir un objeto Exception pasado a su método OnError, debe encapsular la excepción antes generarla. Para este propósito, debe usarse un objeto de excepción estándar.
Recomendaciones adicionales
Intentar anular el registro en el método IObservable<T>.Subscribe puede producir una referencia nula. Por lo tanto, se recomienda evitar esta práctica.
Aunque es posible adjuntar un observador a varios proveedores, el patrón recomendado consiste en adjuntar una instancia de IObserver<T> a una única instancia de IObservable<T>.