Patrón Mediador en Python
El patrón mediador es un patrón de diseño de comportamiento que reduce la dependencia de comunicación directa entre objetos mediante la introducción de un objeto mediador. Imagina la torre de control de un aeropuerto en la vida real: todos los aviones no se comunican directamente entre sí, sino que coordinan despegues, aterrizajes y rutas a través de la torre de control, evitando así el caos y los conflictos.
En el desarrollo de software, cuando existe una relación de interacción compleja entre múltiples objetos, el patrón mediador puede encapsular estas interacciones en el mediador, reduciendo así el acoplamiento entre objetos.
Idea central
La idea central del patrón mediador es"No hablar directamente, sino coordinar a través del mediador"Resuelve la complejidad de las interacciones de muchos a muchos entre objetos, lo que hace que el sistema sea más fácil de mantener y ampliar.
¿Por qué se necesita el patrón mediador?
Escenario del problema
Supongamos que estamos desarrollando un sistema de sala de chat y varios usuarios necesitan enviarse mensajes entre sí. Sin un mediador, el código podría ser así:
Ejemplo
def __init__(self, name):
self.name = name
self.other_users = [] # Necesita conocer a todos los demás usuarios
def send_message(self, message, to_user):
print(f{self.name} envía a {to_user.name}: {message})
to_user.receive_message(message, self)
def receive_message(self, message, from_user):
print(f{self.name} recibe un mensaje de {from_user.name}: {message})
# Ejemplo de uso
user1 = User(Xiaoming)
user2 = User(Xiaohong)
user3 = User(Xiaogang)
# Cada usuario necesita conocer a todos los demás usuarios
user1.other_users = [user2, user3]
user2.other_users = [user1, user3]
user3.other_users = [user1, user2]
user1.send_message("¡Hola!", user2)
Desventajas de este diseño:
- Alto acoplamiento entre objetos: cada usuario necesita conocer a todos los demás.
- Al agregar un nuevo usuario, hay que modificar todos los usuarios existentes.
- Difícil de mantener, la lógica de interacción está dispersa entre los objetos.
Solución
El patrón mediador resuelve este problema introduciendo una sala de chat (mediador):
Ejemplo
def __init__(self):
self.users = {}
def register_user(self, user):
self.users[user.name] = user
user.chat_room = self
def send_message(self, message, from_user, to_user_name=None):
if to_user_name: # Chat privado
if to_user_name in self.users:
self.users[to_user_name].receive_message(message, from_user)
else: # Chat grupal
for user_name, user in self.users.items():
if user_name != from_user.name:
user.receive_message(message, from_user)
class User:
def __init__(self, name):
self.name = name
self.chat_room = None
def send_message(self, message, to_user_name=None):
if self.chat_room:
self.chat_room.send_message(message, self, to_user_name)
def receive_message(self, message, from_user):
print(f{self.name} recibe un mensaje de {from_user.name}: {message})
# Ejemplo de uso
chat_room = ChatRoom()
user1 = User(Xiaoming)
user2 = User(Xiaohong)
user3 = User(Xiaogang)
chat_room.register_user(user1)
chat_room.register_user(user2)
chat_room.register_user(user3)
user1.send_message(¡Hola a todos!) # Mensaje de difusión
user2.send_message(¡Hola Xiaoming!, Xiaoming) # Chat privado
Implementación del patrón mediador
Estructura básica
Entendamos la implementación del patrón mediador con un ejemplo más completo:
Ejemplo
from typing import List
# Mediador abstracto
class Mediator(ABC):
@abstractmethod
def notify(self, sender: object, event: str, data: dict = None):
pass
# Mediador concreto - Sala de chat
class ChatRoomMediator(Mediator):
def __init__(self):
self.users: List[User] = []
def add_user(self, user):
self.users.append(user)
user.set_mediator(self)
def notify(self, sender, event, data=None):
if event == "send_message":
message = data.get("message")
target_user = data.get("target_user")
if target_user: # Chat privado
for user in self.users:
if user.name == target_user:
user.receive_message(message, sender.name)
else: # Chat grupal
for user in self.users:
if user != sender:
user.receive_message(message, sender.name)
elif event == "user_joined":
message = fSistema: {sender.name} se unió a la sala de chat
for user in self.users:
if user != sender:
user.receive_message(message, Sistema)
# Clase de componente base
class BaseComponent:
def __init__(self):
self._mediator = None
def set_mediator(self, mediator: Mediator):
self._mediator = mediator
# Componente concreto - Usuario
class User(BaseComponent):
def __init__(self, name):
super().__init__()
self.name = name
def send_message(self, message, target_user=None):
print(f{self.name} envía un mensaje: {message})
self._mediator.notify(self, "send_message", {
"message": message,
"target_user": target_user
})
def join_chat(self):
self._mediator.notify(self, "user_joined")
def receive_message(self, message, from_name):
print(f{self.name} recibe un mensaje de {from_name}: {message})
# Ejemplo de uso
def main():
# Crear mediador
chat_room = ChatRoomMediator()
# Crear usuario
alice = User("Alice")
bob = User("Bob")
charlie = User("Charlie")
# Registrar usuario en la sala de chat
chat_room.add_user(alice)
chat_room.add_user(bob)
chat_room.add_user(charlie)
print(=== Demostración de sala de chat ===)
# Alice se une a la sala de chat
alice.join_chat()
# Enviar mensaje
alice.send_message(¡Hola a todos, soy Alice!)
bob.send_message(¡Bienvenida Alice!)
charlie.send_message(¡Hola Alice!, "Alice") # Chat privado
# Bob envía un mensaje grupal
bob.send_message(¿Hay alguien en línea?)
if __name__ == "__main__":
main()
Resultado de ejecución
=== 聊天室演示 === 系统: Alice 加入了聊天室 Alice 发送消息: 大家好,我是 Alice! Bob 收到来自 Alice 的消息: 大家好,我是 Alice! Charlie 收到来自 Alice 的消息: 大家好,我是 Alice! Bob 发送消息: 欢迎 Alice! Alice 收到来自 Bob 的消息: 欢迎 Alice! Charlie 收到来自 Bob 的消息: 欢迎 Alice! Charlie 发送消息: Alice 你好! Alice 收到来自 Charlie 的消息: Alice 你好! Bob 发送消息: 有人在线吗? Alice 收到来自 Bob 的消息: 有人在线吗? Charlie 收到来自 Bob 的消息: 有人在线吗?
Estructura UML del patrón mediador

Descripción de la estructura
- Mediator (interfaz del mediador): define la interfaz de comunicación.
- ConcreteMediator (mediador concreto): implementa la lógica de coordinación y conoce todos los componentes.
- BaseComponent (componente base): contiene la referencia al mediador.
- ConcreteComponent (componente concreto): implementa la lógica de negocio concreta.
Escenarios de aplicación real
Escenario 1: Aplicación de GUI
En una interfaz gráfica de usuario, varios controles (botones, cuadros de texto, casillas de verificación, etc.) coordinan sus interacciones a través de un mediador:
Ejemplo
def __init__(self):
self.login_button = None
self.username_input = None
self.password_input = None
self.remember_checkbox = None
def notify(self, sender, event):
if event == "username_changed" or event == "password_changed":
# Al introducir el nombre de usuario o la contraseña, comprobar si se puede habilitar el botón de iniciar sesión.
username = self.username_input.get_text()
password = self.password_input.get_text()
self.login_button.set_enabled(bool(username and password))
elif event == "login_clicked":
# Procesar la lógica de inicio de sesión.
username = self.username_input.get_text()
password = self.password_input.get_text()
remember = self.remember_checkbox.is_checked()
print(fIniciar sesión: {username}, Recordarme: {remember})
elif event == "remember_changed":
print(La opción de recordarme ha cambiado)
class UIComponent:
def __init__(self, mediator=None):
self.mediator = mediator
def set_mediator(self, mediator):
self.mediator = mediator
class Button(UIComponent):
def click(self):
if self.mediator:
self.mediator.notify(self, "login_clicked")
def set_enabled(self, enabled):
print(fBotón {'Habilitar' if enabled else 'Deshabilitar'})
class TextInput(UIComponent):
def __init__(self, mediator=None):
super().__init__(mediator)
self._text = ""
def set_text(self, text):
self._text = text
if self.mediator:
self.mediator.notify(self, "username_changed")
def get_text(self):
return self._text
# Ejemplo de uso
dialog = DialogMediator()
login_btn = Button()
username_input = TextInput()
password_input = TextInput()
dialog.login_button = login_btn
dialog.username_input = username_input
dialog.password_input = password_input
login_btn.set_mediator(dialog)
username_input.set_mediator(dialog)
password_input.set_mediator(dialog)
# Simular la entrada del usuario
username_input.set_text("user123")
password_input.set_text("pass123")
login_btn.click()
Escenario 2: Sistema de pedidos de comercio electrónico
En un sistema de comercio electrónico, el procesamiento de pedidos involucra múltiples subsistemas como inventario, pago, logística, etc.:
Ejemplo
def __init__(self):
self.inventory_system = None
self.payment_system = None
self.shipping_system = None
self.notification_system = None
def place_order(self, order_data):
print(=== Iniciar el procesamiento del pedido ===)
# Comprobar el inventario
if not self.inventory_system.check_stock(order_data):
return Stock insuficiente
# Procesar el pago
payment_result = self.payment_system.process_payment(order_data)
if not payment_result:
return Error en el pago
# Actualizar el inventario
self.inventory_system.update_stock(order_data)
# Organizar el envío
shipping_info = self.shipping_system.schedule_delivery(order_data)
# Enviar notificación
self.notification_system.send_confirmation(order_data, shipping_info)
return Pedido procesado correctamente
class InventorySystem:
def check_stock(self, order_data):
print(Comprobando inventario...)
return True
def update_stock(self, order_data):
print(Actualizando inventario...)
class PaymentSystem:
def process_payment(self, order_data):
print(Procesando pago...)
return True
class ShippingSystem:
def schedule_delivery(self, order_data):
print(Organizando envío...)
return Número de seguimiento: SF123456789
class NotificationSystem:
def send_confirmation(self, order_data, shipping_info):
print(fEnviar correo de confirmación, información de envío: {shipping_info})
# Ejemplo de uso
mediator = OrderMediator()
mediator.inventory_system = InventorySystem()
mediator.payment_system = PaymentSystem()
mediator.shipping_system = ShippingSystem()
mediator.notification_system = NotificationSystem()
order_data = {"product_id": "123", "quantity": 2, "user_id": "user001"}
result = mediator.place_order(order_data)
print(fResultado del pedido: {result})
Ventajas y desventajas del patrón mediador
Ventajas
| Ventajas | Descripción |
|---|---|
| Principio de responsabilidad única | Centraliza la lógica de interacción entre los componentes en el mediador. |
| Principio abierto/cerrado | Se puede introducir un nuevo mediador sin modificar los componentes existentes. |
| Reduce el acoplamiento | Reduce las dependencias directas entre componentes. |
| Fácil de reutilizar | Los componentes individuales se pueden reutilizar más fácilmente en diferentes contextos. |
Desventajas
| Desventajas | Descripción |
|---|---|
| El mediador puede volverse complejo | A medida que aumenta la lógica de interacción, el mediador puede convertirse en un objeto todopoderoso (God Object). |
| Coste de rendimiento | Toda la comunicación pasa a través del mediador, lo que puede introducir un cuello de botella de rendimiento. |
| Dificultad de depuración | La lógica de interacción se concentra en el mediador, por lo que al depurar puede ser necesario rastrear múltiples componentes. |
Mejores prácticas y consideraciones
1. Dividir razonablemente las responsabilidades del mediador
No permitas que un mediador maneje todo; se pueden dividir varios mediadores según el dominio de negocio:
Ejemplo
def notify(self, sender, event, data):
if event == "user_registered":
# Procesar la lógica posterior al registro del usuario
self.send_welcome_email(data)
self.create_user_profile(data)
self.add_to_mailing_list(data)
class OrderProcessingMediator:
def notify(self, sender, event, data):
if event == "order_created":
# Procesar la lógica posterior a la creación del pedido
self.validate_order(data)
self.process_payment(data)
self.update_inventory(data)
2. Evitar que el mediador sea excesivamente complejo
Si el mediador se vuelve demasiado complejo, considera el uso de otros patrones o refactoriza:
Ejemplo
class ComplexMediator:
def handle_event(self, event_type, data):
if event_type == "user_action":
# Gran cantidad de lógica de negocio compleja...
pass
elif event_type == "system_event":
# Más lógica compleja...
pass
# Buenas prácticas: usar el patrón de estrategia para descomponer la lógica compleja
class EventHandler:
def handle(self, data):
pass
class UserActionHandler(EventHandler):
def handle(self, data):
# Manejar acciones de usuario específicas
pass
class SimpleMediator:
def __init__(self):
self.handlers = {}
def register_handler(self, event_type, handler):
self.handlers[event_type] = handler
def notify(self, event_type, data):
if event_type in self.handlers:
self.handlers[event_type].handle(data)
3. Considerar la combinación con el patrón observador
El patrón mediador se usa a menudo junto con el patrón observador:
Ejemplo
from typing import List
class Observer(ABC):
@abstractmethod
def update(self, event, data):
pass
class Observable:
def __init__(self):
self._observers: List[Observer] = []
def add_observer(self, observer):
self._observers.append(observer)
def notify_observers(self, event, data=None):
for observer in self._observers:
observer.update(event, data)
class ChatMediator(Observable):
def send_message(self, message, sender, receiver=None):
# Lógica del mediador
event_data = {
"message": message,
"sender": sender,
"receiver": receiver
}
self.notify_observers("message_sent", event_data)
class LoggingObserver(Observer):
def update(self, event, data):
if event == "message_sent":
print(f"Registro: {data['sender']} envió un mensaje")
Ejercicios
Ejercicio 1: Mejorar el sistema de sala de chat
Extienda el sistema de sala de chat anterior agregando las siguientes funciones:
- Soporte para crear múltiples salas de chat
- Soporte para que los usuarios cambien entre diferentes salas de chat
- Agregar funciones de administrador (expulsar usuarios, silenciar, etc.)
Ejercicio 2: Diseñar un sistema de reserva de billetes de avión
Diseñe un sistema de reserva de boletos de avión usando el patrón mediador, que incluya los siguientes componentes:
- Consulta de vuelos
- Selección de asientos
- Procesamiento de pagos
- Generación de boletos
- Envío de notificaciones
Se requiere que los componentes trabajen de manera coordinada a través del mediador.
Ejercicio 3: Sistema de control de hogar inteligente
Diseñe un sistema de control para hogar inteligente que incluya:
- Control de iluminación
- Regulación de temperatura
- Monitoreo de seguridad
- Modos de escena
Implemente la interconexión inteligente entre dispositivos a través del mediador.
Resumen
El patrón mediador es una herramienta poderosa para manejar interacciones complejas entre objetos. Introduce un centro de coordinación para simplificar la arquitectura del sistema, y es especialmente adecuado para los siguientes escenarios:
- Existen relaciones de interacción complejas en malla entre objetos.
- Se necesita controlar de manera centralizada la lógica de interacción entre múltiples objetos.
- Se desea reducir el acoplamiento entre objetos.
Recuerde que el patrón mediador no es una solución universal. Cuando la lógica de interacción es simple, la comunicación directa entre objetos puede ser más adecuada. Sin embargo, al tratar con sistemas complejos, el uso razonable del patrón mediador puede mejorar significativamente la mantenibilidad y extensibilidad del código.
Puntos clave:
- El mediador encapsula la lógica de interacción entre objetos.
- Los componentes solo se comunican con el mediador y no dependen directamente entre sí.
- El mediador puede volverse complejo, por lo que requiere un diseño razonable.
- Combinarlo con otros patrones (como el observador) puede producir mejores resultados.