Comunicación entre componentes débilmente acoplados
Nota:
Este libro electrónico se publicó en primavera de 2017 y no se ha actualizado desde entonces. Hay mucho en el libro que 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 Xamarin.FormsMessagingCenter
clase implementa el patrón publish-subscribe, lo que permite la comunicación basada en mensajes entre componentes que no son convenientes para vincular por referencias de objeto y tipo. 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: En la figura 4-1 se muestra 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 MessagingCenter
clase para comunicarse entre componentes de acoplamiento flexible. 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, laBasketViewModel
clase se suscribe al mensaje e incrementa el número de artículos de la cesta de la compra en respuesta. Además, la clase también cancela laBasketViewModel
suscripción de este mensaje. - El
Filter
mensaje lo publica laCatalogViewModel
clase cuando el usuario aplica un filtro de marca o tipo a los elementos mostrados desde el catálogo. A cambio, laCatalogView
clase 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
ChangeTab
clase publica elMainViewModel
mensaje cuandoCheckoutViewModel
navega a laMainViewModel
siguiente creación y envío correctos de un nuevo pedido. A cambio, laMainView
clase se suscribe al mensaje y actualiza la interfaz de usuario para que la pestaña Mi perfil esté activa, para 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, MessagingCenter
se usa 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
Serializar el subproceso de la 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
, vea 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 Send
método 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 una
CatalogItem
instancia de .
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.
Suscribirse 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 muestra cómo se suscribe la aplicación móvil eShopOnContainers y los procesos, el AddProduct
mensaje:
MessagingCenter.Subscribe<CatalogViewModel, CatalogItem>(
this, MessageKeys.AddProduct, async (sender, arg) =>
{
BadgeCount++;
await AddCatalogItemAsync(arg);
});
En este ejemplo, el Subscribe
método se suscribe al AddProduct
mensaje 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 un delegado de devolución de llamada porque varios subprocesos podrían tener acceso 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á AddProduct
mensajes enviados desde la CatalogViewModel
clase , cuyos datos de carga son una CatalogItem
instancia de .
Anulación de la suscripción de 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 Unsubscribe
método refleja los argumentos de tipo especificados al suscribirse para recibir el AddProduct
mensaje.
Resumen
La Xamarin.FormsMessagingCenter
clase implementa el patrón publish-subscribe, lo que permite la comunicación basada en mensajes entre componentes que no son convenientes para vincular por referencias de objeto y tipo. 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.