1. Información general
En este artículo, veremos el Patrón de mediador, uno de los patrones de comportamiento del GoF . Describiremos su propósito y explicaremos cuándo debemos usarlo.
Como de costumbre, también proporcionaremos un ejemplo de código simple.
2. Patrón de mediador
En la programación orientada a objetos, siempre debemos intentar diseñar el sistema de tal manera que los componentes estén acoplados libremente y sean reutilizables . Este enfoque hace que nuestro código sea más fácil de mantener y probar.
En la vida real, sin embargo, a menudo necesitamos lidiar con un conjunto complejo de objetos dependientes. Aquí es cuando el Patrón de mediador puede resultar útil.
La intención del patrón de mediador es reducir la complejidad y las dependencias entre objetos estrechamente acoplados que se comunican directamente entre sí . Esto se logra creando un objeto mediador que se encarga de la interacción entre los objetos dependientes. En consecuencia, toda la comunicación pasa por el mediador.
Esto promueve un acoplamiento flojo, ya que un conjunto de componentes que trabajan juntos ya no tienen que interactuar directamente. En cambio, solo se refieren al objeto mediador único. De esta forma, también es más fácil reutilizar estos objetos en otras partes del sistema.
3. Diagrama UML de Mediator Pattern
Veamos ahora el patrón visualmente:

En el diagrama UML anterior, podemos identificar a los siguientes participantes:
- El mediador define la interfaz que utilizan los objetos Colega para comunicarse
- Colega define la clase abstracta que tiene una única referencia al Mediador
- ConcreteMediator encapsula la lógica de interacción entre los objetos Colleague
- ConcreteColleague1 y ConcreteColleague2 se comunican solo a través del Mediador
Como podemos ver, los objetos Colega no se refieren directamente entre sí. En cambio, toda la comunicación la realiza el Mediador .
En consecuencia, ConcreteColleague1 y ConcreteColleague2 se pueden reutilizar más fácilmente.
Además, en caso de que necesitemos cambiar la forma en que los objetos Colega trabajan juntos, solo tenemos que modificar la lógica de ConcreteMediator . O podemos crear una nueva implementación del Mediador.
4. Implementación de Java
Ahora que tenemos una idea clara de la teoría, veamos un ejemplo para comprender mejor el concepto en la práctica.
4.1. Escenario de ejemplo
Imagínese que estamos construyendo un sistema de enfriamiento simple que consta de un ventilador, una fuente de alimentación y un botón. Al presionar el botón se encenderá o apagará el ventilador. Antes de encender el ventilador, debemos encenderlo. Del mismo modo, tenemos que apagar la energía inmediatamente después de que se apaga el ventilador.
Echemos ahora un vistazo a la implementación de ejemplo:
public class Button { private Fan fan; // constructor, getters and setters public void press(){ if(fan.isOn()){ fan.turnOff(); } else { fan.turnOn(); } } }
public class Fan { private Button button; private PowerSupplier powerSupplier; private boolean isOn = false; // constructor, getters and setters public void turnOn() { powerSupplier.turnOn(); isOn = true; } public void turnOff() { isOn = false; powerSupplier.turnOff(); } }
public class PowerSupplier { public void turnOn() { // implementation } public void turnOff() { // implementation } }
A continuación, probemos la funcionalidad:
@Test public void givenTurnedOffFan_whenPressingButtonTwice_fanShouldTurnOnAndOff() { assertFalse(fan.isOn()); button.press(); assertTrue(fan.isOn()); button.press(); assertFalse(fan.isOn()); }
Todo parece funcionar bien. Pero observe cómo las clases Button, Fan y PowerSupplier están estrechamente relacionadas . El botón funciona directamente en el ventilador y el ventilador interactúa tanto con el botón como con el proveedor de energía.
Sería difícil reutilizar la clase Button en otros módulos. Además, si necesitamos agregar una segunda fuente de alimentación a nuestro sistema, entonces tendríamos que modificar la lógica de la clase Fan .
4.2. Agregar el patrón de mediador
Ahora, implementemos el patrón de mediador para reducir las dependencias entre nuestras clases y hacer que el código sea más reutilizable.
Primero, presentemos la clase Mediator :
public class Mediator { private Button button; private Fan fan; private PowerSupplier powerSupplier; // constructor, getters and setters public void press() { if (fan.isOn()) { fan.turnOff(); } else { fan.turnOn(); } } public void start() { powerSupplier.turnOn(); } public void stop() { powerSupplier.turnOff(); } }
A continuación, modifiquemos las clases restantes:
public class Button { private Mediator mediator; // constructor, getters and setters public void press() { mediator.press(); } }
public class Fan { private Mediator mediator; private boolean isOn = false; // constructor, getters and setters public void turnOn() { mediator.start(); isOn = true; } public void turnOff() { isOn = false; mediator.stop(); } }
Nuevamente, probemos la funcionalidad:
@Test public void givenTurnedOffFan_whenPressingButtonTwice_fanShouldTurnOnAndOff() { assertFalse(fan.isOn()); button.press(); assertTrue(fan.isOn()); button.press(); assertFalse(fan.isOn()); }
Nuestro sistema de enfriamiento funciona como se esperaba.
Ahora que hemos implementado el patrón de mediador, ninguna de las clases Button , Fan o PowerSupplier se comunica directamente . Solo tienen una única referencia al Mediador.
Si necesitamos agregar una segunda fuente de alimentación en el futuro, todo lo que tenemos que hacer es actualizar la lógica de Mediator ; Las clases Button y Fan permanecen intactas.
Este ejemplo muestra la facilidad con la que podemos separar los objetos dependientes y hacer que nuestro sistema sea más fácil de mantener.
5. Cuándo usar el patrón de mediador
The Mediator Pattern is a good choice if we have to deal with a set of objects that are tightly coupled and hard to maintain. This way we can reduce the dependencies between objects and decrease the overall complexity.
Additionally, by using the mediator object, we extract the communication logic to the single component, therefore we follow the Single Responsibility Principle. Furthermore, we can introduce new mediators with no need to change the remaining parts of the system. Hence, we follow the Open-Closed Principle.
Sometimes, however, we may have too many tightly coupled objects due to the faulty design of the system. If this is a case, we should not apply the Mediator Pattern. Instead, we should take one step back and rethink the way we've modeled our classes.
Al igual que con todos los demás patrones, debemos considerar nuestro caso de uso específico antes de implementar ciegamente el Patrón de mediador .
6. Conclusión
En este artículo, aprendimos sobre el patrón de mediador. Explicamos qué problema resuelve este patrón y cuándo deberíamos considerar su uso. También implementamos un ejemplo simple del patrón de diseño.
Como siempre, las muestras de código completas están disponibles en GitHub.