Mediator Pattern en C#
Introducción
El Patrón Mediador o Mediator Pattern es un Behavioral Pattern o Patrón de Comportamiento que permite la comunicación de varios objetos entre sí, sin que ninguno de esos objetos tenga que conocer la estructura de los otros.
Como mediador o coordinador, es el encargado de manejar la comunicación o tráfico entre las partes que forman la estructura principal, y que habremos definido en la lógica de nuestro código previamente.
Cuando un objeto necesita comunicar algo a otro u otros objetos, no necesita comunicarse con cada objeto.
Simplemente, le comunica al mediador algo, y éste, se encarga de enrutar y comunicarse con el resto de objetos.
El patrón tiene además dos ventajas adicionales.
Hace que su mantenimiento sea sencillo, y posee un acoplamiento débil.
Diagrama del patrón
El patrón Mediador, queda definido por la siguiente imagen:
De acuerdo a la imagen, tenemos que:
Mediator define una interfaz (o incluso clase abstracta) que establece la comunicación de los objetos Colleague.
ConcreteMediator implementa el comportamiento de la interfaz para coordinar los objetos Colleague.
Es el encargado de conocer todos los objetos ConcreteColleague, así cómo mantenerlos, gestionarlos, etc.
Colleague representa a cada clase Colleague, la cual conoce a su vez a su mediador.
Cada clase concreta de Colleague, implementa la clase Colleague y se comunica por lo tanto con su mediador, que a su vez establecerá la comunicación con los otros objetos Colleague.
Implementación del patrón en C#
Como siempre, lo que tenemos que hacer es aterrizar el patrón al suelo, por lo que voy a escribir código en C# que nos permita comprender mejor cómo funciona.
Voy a implementar el uso de este patrón en lo que es una supuesta red social, grupo de chat, etc., que usamos casi diariamente.
El patrón en sí es bastante más simple, pero voy a tratar de darle más empaque si cabe, para que el patrón sea más comprensible en un teórico ejemplo real.
Como decía antes, debemos preparar una interfaz IMediator.
En mi caso, he creado una interfaz de nombre ISocialNetworkGroupMediator.
public interface IMediator { string GetGroupName(); void RegisterUser(User user); void SendMessage(User user, string message); }
Esta interfaz nos servirá para indicar a la clase que la implemente, un nombre para el grupo, el registro de cada uno de los usuarios, y la posibilidad de enviar un mensaje a los integrantes del grupo.
Lógicamente, el usuario que envíe el mensaje, no debería recibirlo, pero esa lógica la implementaremos más adelante.
A continuación, crearemos una clase que implemente la interfaz.
public class Mediator : IMediator { private string _groupName = String.Empty; private List<User> _users = new List<User>(); public Mediator(string groupName) => _groupName = groupName; public string GetGroupName() => _groupName; public void RegisterUser(User user) { _users.Add(user); Console.WriteLine($"\t{user.GetUserName()} (has joined to the group '{_groupName}'!)"); } public void SendMessage(User user, string message) { foreach (var userGroup in _users) if (userGroup != user) userGroup.Receive(message); } }
Aquí implementamos los tres métodos principales de la interfaz que, como decía anteriormente, sirve para recuperar el nombre del grupo, registrar un usuario, y enviar un mensaje a los otros usuarios.
Podríamos «complicar» más aún la lógica para gestionar los usuarios (alta, baja, deshabilitar, etc).
El siguiente paso, será el de declarar la clase abstracta User, para sobre ella, implementar la clase concreta de usuario por cada usuario que creemos.
public abstract class User { protected IMediator Mediator; protected string Name; public User(IMediator mediator, string name) { Mediator = mediator; Name = name; } public abstract void Send(string message); public abstract void Receive(string message); public string GetUserName() => Name; }
Esta clase abstracta, recibe en su constructor el mediador y el nombre del usuario como identificador general en este ejemplo, y luego dos métodos abstractos que tendremos implementar, uno para enviar mensajes a otros usuarios, y otro para recibir sus mensajes, además de poder obtener el nombre del usuario.
Así que la clase concreta que será utilizada por cada uno de los usuarios que registremos, será de esta manera:
public class ConcreteUser : User { public ConcreteUser(IMediator mediator, string name) : base(mediator, name) { } public override void Receive(string message) => Console.WriteLine($"\t{Name} (Received Message) > {message}"); public override void Send(string message) { Console.WriteLine($"{Name} (Sending Message) > {message}\n"); mediator.SendMessage(this, message); } }
Consumiendo el patrón
Por último, vamos a consumir el patrón y hacerlo funcionar.
Nuestro código de ejemplo quedará de la siguiente forma:
IMediator mediator = new Mediator("Patterns for C# Group"); Console.WriteLine($"GROUP '{mediator.GetGroupName()}' CREATED!\n"); User jorge = new ConcreteUser(mediator, "Jorge"); User maria = new ConcreteUser(mediator, "María"); User carlos = new ConcreteUser(mediator, "Carlos"); mediator.RegisterUser(jorge); mediator.RegisterUser(maria); mediator.RegisterUser(carlos); Console.WriteLine(); maria.Send("Hi all!"); Console.WriteLine(); carlos.Send("Wow!, I love this group!"); Console.WriteLine(); jorge.Send("Hi María and Carlos!");
Y nuestro ejemplo en ejecución tendrá un aspecto similar al siguiente:
En mi cuenta de GitHub, puedes acceder a un ejemplo más completo del uso de este patrón.
Accederás a este código en este enlace.
El diagrama de las clases de C# que encontrarás en GitHub es el siguiente:
Happy Coding!