Compartir vía


Comunicación entre componentes débilmente acoplados

Nota:

Este libro electrónico se publicó en la primavera de 2017 y no se ha actualizado desde entonces. Mucho contenido del libro sigue siendo valioso, pero algunos de los materiales están obsoletos.

El patrón de publicación y suscripción es un patrón de mensajería en el que los publicadores envían mensajes sin tener conocimiento de los destinatarios, que se conocen como suscriptores. Del mismo modo, los suscriptores escuchan mensajes específicos, sin tener conocimiento de ningún publicador.

Los eventos de .NET implementan el patrón de publicación y suscripción, y son el enfoque más sencillo para una capa de comunicación entre los componentes si no se requiere el acoplamiento flexible, como sucede con un control y la página que lo contiene. Pero las duraciones del publicador y del suscriptor están acopladas por referencias a objetos, y el tipo de suscriptor debe tener una referencia al tipo de publicador. Esto puede generar problemas de administración de memoria, en especial cuando hay objetos de corta duración que se suscriben a un evento de un objeto estático o de larga duración. Si no se quita el controlador de eventos, el suscriptor se mantendrá activo mediante la referencia a él en el publicador y esto impedirá o retrasará la recolección de elementos no utilizados del suscriptor.

Introducción a MessagingCenter

La clase Xamarin.FormsMessagingCenter implementa el patrón de publicación y suscripción, lo que permite la comunicación basada en mensajes entre los componentes que no se recomienda vincular mediante referencias de tipo y objeto. Este mecanismo permite a los publicadores y suscriptores comunicarse sin que exista una referencia entre sí, lo que ayuda a reducir las dependencias entre los componentes, a la vez que permite desarrollar y probar esos componentes de forma independiente.

La clase MessagingCenter proporciona la funcionalidad de publicación y suscripción de multidifusión. Esto significa que puede haber varios publicadores que publican un único mensaje y varios suscriptores que escuchen el mismo mensaje: La figura 4-1 ilustra esta relación:

Funcionalidad de publicación y suscripción de multidifusión

Figura 4-1: funcionalidad de publicación y suscripción de multidifusión

Los publicadores envían mensajes con el método MessagingCenter.Send, mientras que los suscriptores escuchan mensajes con el método MessagingCenter.Subscribe. Además, en caso necesario, los suscriptores también pueden cancelar la suscripción a mensajes con el método MessagingCenter.Unsubscribe.

Internamente, la clase MessagingCenter utiliza referencias débiles. Esto significa que no mantendrá los objetos activos y permitirá la recolección de elementos no utilizados. Por lo tanto, solo debería ser necesario cancelar la suscripción a un mensaje cuando una clase ya no quiere recibir el mensaje.

La aplicación móvil eShopOnContainers usa la clase MessagingCenter para comunicarse entre componentes de acoplamiento impreciso. La aplicación define tres mensajes:

  • La clase CatalogViewModel publica el mensaje AddProduct cuando se agrega un elemento a la cesta de la compra. A cambio, la clase BasketViewModel se suscribe al mensaje e incrementa el número de artículos de la cesta de la compra en respuesta. Además, la clase BasketViewModel también finaliza la suscripción a este mensaje.
  • La clase CatalogViewModel publica el mensaje Filter cuando el usuario aplica un filtro de marca o tipo a los elementos mostrados desde el catálogo. A cambio, la clase CatalogView se suscribe al mensaje y actualiza la interfaz de usuario para que solo se muestren los elementos que coincidan con los criterios de filtro.
  • La clase MainViewModel publica el mensaje ChangeTab cuando CheckoutViewModel navega a MainViewModel tras la creación y el envío correctos de un nuevo pedido. A cambio, la clase MainView se suscribe al mensaje y actualiza la interfaz de usuario para que la pestaña Mi perfil esté activa y mostrar los pedidos del usuario.

Nota:

Aunque la clase MessagingCenter permite la comunicación entre clases de acoplamiento impreciso, no ofrece la única solución arquitectónica a este problema. Por ejemplo, la comunicación entre un modelo de vista y una vista también se puede lograr mediante el motor de enlace y a través de notificaciones de cambio de propiedad. Asimismo, la comunicación entre dos modelos de vista también se puede lograr pasando datos durante la navegación.

En la aplicación móvil eShopOnContainers, se usa MessagingCenter para actualizar en la interfaz de usuario en respuesta a una acción que se produce en otra clase. Por lo tanto, los mensajes se publican en el subproceso de la interfaz de usuario, con suscriptores que reciben el mensaje en el mismo subproceso.

Sugerencia

Serialice en el subproceso de interfaz de usuario al realizar actualizaciones de la interfaz de usuario. Si para actualizar la interfaz de usuario se requiere un mensaje enviado desde un subproceso en segundo plano, procese dicho mensaje en el subproceso de interfaz de usuario del suscriptor, invocando para ello el método Device.BeginInvokeOnMainThread.

Para obtener más información sobre MessagingCenter, consulte MessagingCenter.

Definición de un mensaje

Los mensajes MessagingCenter son cadenas que se usan para identificar mensajes. En el ejemplo de código siguiente se muestran los mensajes definidos en la aplicación móvil eShopOnContainers:

public class MessageKeys  
{  
    // Add product to basket  
    public const string AddProduct = "AddProduct";  

    // Filter  
    public const string Filter = "Filter";  

    // Change selected Tab programmatically  
    public const string ChangeTab = "ChangeTab";  
}

En este ejemplo, los mensajes se definen mediante constantes. La ventaja de este método reside en que ofrece seguridad de los tipos en tiempo de compilación y compatibilidad con la refactorización.

Publicación de un mensaje

Los publicadores informan a los suscriptores de un mensaje con una de las sobrecargas de MessagingCenter.Send. En el ejemplo de código siguiente se muestra cómo publicar el mensaje AddProduct:

MessagingCenter.Send(this, MessageKeys.AddProduct, catalogItem);

En este ejemplo, el método Send especifica tres argumentos:

  • El primer argumento especifica la clase del emisor. La clase del emisor la deben especificar aquellos suscriptores que quieran recibir el mensaje.
  • El segundo argumento especifica el mensaje.
  • El tercer argumento especifica los datos de carga que se van a enviar al suscriptor. En este caso, los datos de carga son un elemento CatalogItem.

El método Send publicará el mensaje y sus datos de carga mediante un enfoque del tipo "dispara y olvida". Por lo tanto, el mensaje se envía incluso aunque no haya ningún suscriptor registrado para recibir el mensaje. En esta situación, se omite el mensaje enviado.

Nota:

El método MessagingCenter.Send puede usar parámetros genéricos para controlar cómo se entregan los mensajes. En consecuencia, distintos suscriptores podrán recibir varios mensajes que compartan una identidad de mensaje, pero que envíen diferentes tipos de datos de carga.

Suscripción a un mensaje

Los suscriptores pueden registrarse para recibir un mensaje mediante una de las sobrecargas de MessagingCenter.Subscribe. En el ejemplo de código siguiente se describe cómo la aplicación móvil eShopOnContainers se suscribe al mensaje AddProduct y lo procesa:

MessagingCenter.Subscribe<CatalogViewModel, CatalogItem>(  
    this, MessageKeys.AddProduct, async (sender, arg) =>  
{  
    BadgeCount++;  

    await AddCatalogItemAsync(arg);  
});

En este ejemplo, el método Subscribe se suscribe al mensaje AddProduct y ejecuta un delegado de devolución de llamada en respuesta a la recepción del mensaje. Este delegado de devolución de llamada, especificado como una expresión lambda, ejecuta código que actualiza la interfaz de usuario.

Sugerencia

Considere la posibilidad de usar datos de carga inmutables. No intente modificar los datos de carga desde dentro de un delegado de devolución de llamada, ya que varios subprocesos podrían estar accediendo a los datos recibidos simultáneamente. En este escenario, los datos de carga deben ser inmutables para evitar errores de simultaneidad.

Es posible que un suscriptor no necesite controlar cada instancia de un mensaje publicado, lo cual se puede controlar mediante los argumentos de tipo genérico que se especifican en el método Subscribe. En este ejemplo, el suscriptor solo recibirá mensajes AddProduct enviados desde la clase CatalogViewModel, cuyos datos de carga son una instancia CatalogItem.

Cancelación de la suscripción a un mensaje

Los suscriptores pueden cancelar la suscripción a los mensajes que ya no quieren recibir. Esto se logra con uno de las sobrecargas MessagingCenter.Unsubscribe, como se muestra en el ejemplo de código siguiente:

MessagingCenter.Unsubscribe<CatalogViewModel, CatalogItem>(this, MessageKeys.AddProduct);

En este ejemplo, la sintaxis del método Unsubscribe refleja los argumentos de tipo especificados al suscribirse para recibir el mensaje AddProduct.

Resumen

La clase Xamarin.FormsMessagingCenter implementa el patrón de publicación y suscripción, lo que permite la comunicación basada en mensajes entre los componentes que no se recomienda vincular mediante referencias de tipo y objeto. Este mecanismo permite a los publicadores y suscriptores comunicarse sin que exista una referencia entre sí, lo que ayuda a reducir las dependencias entre los componentes, a la vez que permite desarrollar y probar esos componentes de forma independiente.