Compartir a través de


Comunicación entre componentes acoplados de forma imprecisa

Sugerencia

Este contenido es un extracto del libro electrónico "Patrones de aplicaciones empresariales con .NET MAUI", disponible en Documentación de .NET o como un PDF descargable y gratuito que se puede leer sin conexión.

Miniatura de la portada del libro electrónico

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 MVVM Toolkit Messenger

La interfaz de MVVM Toolkit IMessenger 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 directa 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.

Nota

MVVM Toolkit Messenger forma parte del paquete de CommunityToolkit.Mvvm. Para obtener información sobre cómo agregar el paquete al proyecto, consulte la Introducción MVVM Toolkit en el Centro para desarrolladores de Microsoft.

Advertencia

.NET MAUI contiene una clase de MessagingCenter integrada que ya no se recomienda para su uso. Use MVVM Toolkit Messenger en su lugar.

La interfaz de IMessenger permite la funcionalidad de publicación y suscripción de multidifusión. Esto significa que puede haber varios publicadores que publican un único mensaje, y puede haber varios suscriptores que escuchen el mismo mensaje. En la siguiente imagen se ilustra esta relación:

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

Hay dos implementaciones de la interfaz de IMessenger que vienen con el paquete de CommunityToolkit.Mvvm. WeakReferenceMessenger usa referencias débiles, que pueden dar lugar a una limpieza más sencilla para los suscriptores de mensajes. Es una buena opción si los suscriptores no tienen un ciclo de vida claramente definido. StrongReferenceMessenger usa referencias seguras que pueden dar lugar a un mejor rendimiento y una duración más claramente controlada de la suscripción. Si tiene un flujo de trabajo con una duración muy controlada (por ejemplo, una suscripción enlazada a los métodos OnAppearing y OnDisappearing de una página), StrongReferenceManager puede ser una opción mejor, si el rendimiento es un problema. Ambas implementaciones están disponibles con implementaciones predeterminadas listas para usar haciendo referencia a WeakReferenceMessenger.Default o StrongReferenceMessenger.Default.

Nota

Aunque la interfaz IMessenger 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.

La aplicación multiplataforma eShop usa la clase WeakReferenceMessenger para comunicarse entre componentes de acoplamiento impreciso. La aplicación define un solo mensaje denominado AddProductMessage. La clase CatalogViewModel publica AddProductMessage cuando se agrega un elemento a la cesta de la compra. A cambio, la clase CatalogView se suscribe al mensaje y lo usa en respuesta para resaltar las adiciones de productos con una animación.

En la aplicación multiplataforma eShop, se usa WeakReferenceMessenger 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 desde el subproceso en el que se ejecuta la clase, con suscriptores que reciben el mensaje en el mismo subproceso.

Sugerencia

Serialice en la interfaz de usuario o en el subproceso principal al realizar actualizaciones de interfaz de usuario. Si las actualizaciones de interfaces de usuario no se realizan en este subproceso, puede suceder que la aplicación se bloquee o se vuelva inestable.

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 MainThread.BeginInvokeOnMainThread.

Para obtener más información sobre Messenger, consulte Messenger en el Centro para desarrolladores de Microsoft.

Definición de un mensaje

Los mensajes de IMessenger son objetos personalizados que proporcionan cargas personalizadas. En el ejemplo de código siguiente se muestra el mensaje AddProductMessage definido en la aplicación multiplataforma eShop:

public class AddProductMessage : ValueChangedMessage<int>
{
    public AddProductMessage(int count) : base(count)
    {
    }
}

La clase base se define mediante ValueChangedMessage<T>, donde T puede ser de cualquier tipo necesario para pasar datos. Tanto los publicadores de mensajes como los suscriptores pueden esperar mensajes de un tipo específico (por ejemplo, AddProductMessage). Esto puede ayudar a garantizar que ambas partes hayan acordado un contrato de mensajería y que los datos proporcionados con ese contrato sean coherentes. Además, este enfoque proporciona compatibilidad con la seguridad y la refactorización de tipos en tiempo de compilación.

Publicación de un mensaje

Para publicar un mensaje, es necesario usar el método IMessenger.Send. Se puede acceder a esto con más frecuencia a través de WeakReferenceMessenger.Default.Send o StrongReferenceMessenger.Default.Send. El mensaje enviado puede ser de cualquier tipo de objeto. En el ejemplo de código siguiente se muestra cómo publicar el mensaje AddProduct:

WeakReferenceMessenger.Default.Send(new Messages.AddProductMessage(BadgeCount));

En este ejemplo, el método Send especifica que se proporciona una nueva instancia del objeto AddProductMessage para la recepción de los suscriptores descendentes. Se puede agregar un segundo parámetro de token adicional para usarlo cuando varios suscriptores diferentes necesiten recibir mensajes del mismo tipo sin recibir el mensaje incorrecto.

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.

Suscripción a un mensaje

Los suscriptores pueden registrarse para recibir un mensaje mediante una de las sobrecargas de IMessenger.Register<T>. En el ejemplo de código siguiente se describe cómo la aplicación multiplataforma eShop se suscribe al mensaje AddProductMessage y lo procesa:

WeakReferenceMessenger.Default
    .Register<CatalogView, Messages.AddProductMessage>(
        this,
        async (recipient, message) =>
        {
            await recipient.Dispatcher.DispatchAsync(
                async () =>
                {
                    await recipient.badge.ScaleTo(1.2);
                    await recipient.badge.ScaleTo(1.0);
                });
        });

En el ejemplo anterior, el método Register se suscribe al mensaje AddProductMessage 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.

Nota

Evite el uso de this dentro del delegado de devolución de llamada para evitar capturar ese objeto dentro del delegado. Esto puede ayudar a mejorar el rendimiento. En su lugar, use el parámetro recipient.

Si se proporcionan datos de carga, no intente modificarlos 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.

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 IMessenger.Unregister, como se muestra en el ejemplo de código siguiente:

WeakReferenceMessenger.Default.Unregister<Messages.AddProductMessage>(this);

Nota

En este ejemplo, no es totalmente necesario llamar a Unregister, ya que WeakReferenceMessenger permitirá la recolección residual de los objetos no utilizados. Si se usara StrongReferenceMessenger, se recomienda llamar a Unregister para cualquier suscripción que ya no esté en uso.

En este ejemplo, la sintaxis del método Unsubscribe especifica el argumento de tipo del mensaje y el objeto receptor que escucha los mensajes.

Resumen

La interfaz de MVVM Toolkit IMessenger 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.