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:
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 mensajeAddProduct
cuando se agrega un elemento a la cesta de la compra. A cambio, la claseBasketViewModel
se suscribe al mensaje e incrementa el número de artículos de la cesta de la compra en respuesta. Además, la claseBasketViewModel
también finaliza la suscripción a este mensaje. - La clase
CatalogViewModel
publica el mensajeFilter
cuando el usuario aplica un filtro de marca o tipo a los elementos mostrados desde el catálogo. A cambio, la claseCatalogView
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 mensajeChangeTab
cuandoCheckoutViewModel
navega aMainViewModel
tras la creación y el envío correctos de un nuevo pedido. A cambio, la claseMainView
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.