Patron de médiateur Python
Le patron Médiateur est un patron de conception comportemental qui réduit les dépendances de communication directe entre les objets en introduisant un objet médiateur. Imaginez une tour de contrôle d'aéroport dans la vie réelle : tous les avions ne communiquent pas directement entre eux, mais coordonnent les décollages, les atterrissages et les itinéraires via la tour de contrôle, évitant ainsi le chaos et les conflits.
Dans le développement logiciel, lorsqu'il existe des relations d'interaction complexes entre plusieurs objets, le patron Médiateur peut encapsuler ces interactions dans le médiateur, réduisant ainsi le couplage entre les objets.
Idée centrale
L'idée centrale du patron Médiateur est« Ne parlez pas directement ; coordonnez-vous via un médiateur. »Il résout la complexité des interactions plusieurs-à-plusieurs entre les objets, ce qui rend le système plus facile à maintenir et à étendre.
Pourquoi avons-nous besoin du patron Médiateur ?
Scénario problématique
Supposons que nous développions un système de salon de discussion avec plusieurs utilisateurs qui doivent s'envoyer des messages entre eux. Sans médiateur, le code pourrait ressembler à ceci :
Exemple
def __init__(self, name):
self.name = name
self.other_users = [] # Il faut connaître tous les autres utilisateurs
def send_message(self, message, to_user):
print(f"{self.name} a envoyé à {to_user.name} : {message}")
to_user.receive_message(message, self)
def receive_message(self, message, from_user):
print(f{self.name} a reçu un message de {from_user.name} : {message})
# Exemple d'utilisation
user1 = User(Xiaoming)
user2 = User(Xiaohong)
user3 = User(Xiaogang)
# Chaque utilisateur doit connaître tous les autres utilisateurs
user1.other_users = [user2, user3]
user2.other_users = [user1, user3]
user3.other_users = [user1, user2]
user1.send_message(Bonjour !, user2)
Inconvénients de cette conception :
- Couplage élevé entre les objets ; chaque utilisateur doit connaître tous les autres utilisateurs.
- L'ajout d'un nouvel utilisateur nécessite de modifier tous les utilisateurs existants.
- Difficile à maintenir ; la logique d'interaction est dispersée entre les objets.
Solution
Le patron Médiateur résout ce problème en introduisant un salon de discussion (médiateur) :
Exemple
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=Aucune):
if to_user_name: # Chat privé
if to_user_name in self.users:
self.users[to_user_name].receive_message(message, from_user)
else: # Chat de groupe
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} a reçu un message de {from_user.name} : {message})
# Exemple d'utilisation
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(Bonjour tout le monde !) # Message diffusé
user2.send_message(Bonjour Xiaoming !, Xiaoming) # Chat privé
Implémentation du patron Médiateur
Structure de base
Comprenons l'implémentation du patron Médiateur à travers un exemple plus complet :
Exemple
from typing import List
# Médiateur abstrait
class Mediator(ABC):
@abstractmethod
def notify(self, sender: object, event: str, data: dict = None):
pass
# Médiateur concret - Salon de discussion
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=Aucune):
if event == "send_message":
message = data.get("message")
target_user = data.get("target_user")
if target_user: # Discussion privée
for user in self.users:
if user.name == target_user:
user.receive_message(message, sender.name)
else: # Chat de groupe
for user in self.users:
if user != sender:
user.receive_message(message, sender.name)
elif event == "user_joined":
message = fSystème: {sender.name} a rejoint le salon de discussion
for user in self.users:
if user != sender:
user.receive_message(message, System)
# Classe de composant de base
class BaseComponent:
def __init__(self):
self._mediator = None
def set_mediator(self, mediator: Mediator):
self._mediator = mediator
# Composant concret - User
class User(BaseComponent):
def __init__(self, name):
super().__init__()
self.name = name
def send_message(self, message, target_user=None):
print(f{self.name} a envoyé un message : {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} a reçu un message de {from_name} : {message})
# Exemple d'utilisation
def main():
# Créer le médiateur
chat_room = ChatRoomMediator()
# Créer un utilisateur
alice = User("Alice")
bob = User("Bob")
charlie = User("Charlie")
# Enregistrer les utilisateurs dans le salon de discussion
chat_room.add_user(alice)
chat_room.add_user(bob)
chat_room.add_user(charlie)
print(=== Démo du salon de discussion ===)
# Alice rejoint le salon de discussion
alice.join_chat()
# Envoyer un message
alice.send_message(Bonjour à tous, je suis Alice !)
bob.send_message(Bienvenue Alice !)
charlie.send_message(Bonjour Alice !, "Alice") # Discussion privée
# Bob envoie un message de groupe
bob.send_message(Y a-t-il quelqu'un en ligne ?)
if __name__ == "__main__":
main()
Sortie
=== 聊天室演示 === 系统: 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 的消息: 有人在线吗?
Structure UML du patron Médiateur

Description de la structure
- Médiateur (interface médiateur): définit l'interface de communication
- ConcreteMediator (médiateur concret): implémente la logique de coordination et connaît tous les composants
- BaseComponent (composant de base): contient une référence au médiateur
- ConcreteComponent (composant concret): implémente la logique métier concrète
Scénarios d'application concrets
Scénario 1 : Application GUI
Dans une interface utilisateur graphique, divers contrôles (boutons, zones de texte, cases à cocher, etc.) coordonnent les interactions par l'intermédiaire d'un médiateur :
Exemple
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":
# Lorsque le nom d'utilisateur ou le mot de passe est saisi, vérifier si le bouton de connexion peut être activé
username = self.username_input.get_text()
password = self.password_input.get_text()
self.login_button.set_enabled(bool(username and mot de passe))
elif event == "login_clicked":
# Gérer la logique de connexion
username = self.username_input.get_text()
password = self.password_input.get_text()
remember = self.remember_checkbox.is_checked()
print(fConnexion : {username}, Se souvenir de moi : {remember})
elif event == "remember_changed":
print(L'option Se souvenir de moi a changé)
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(fButton {'Activer' if enabled else 'Désactiver'})
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
# Exemple d'utilisation
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)
# Simuler la saisie utilisateur
username_input.set_text("user123")
password_input.set_text("pass123")
login_btn.click()
Scénario 2 : Système de commande e-commerce
Dans un système de commerce électronique, le traitement des commandes implique plusieurs sous-systèmes tels que les stocks, le paiement et la logistique :
Exemple
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(=== Début du traitement de la commande ===)
# Vérifier l'inventaire
if not self.inventory_system.check_stock(order_data):
return Stock insuffisant
# Traiter le paiement
payment_result = self.payment_system.process_payment(order_data)
if not payment_result:
return Paiement échoué
# Mettre à jour l'inventaire
self.inventory_system.update_stock(order_data)
# Organiser l'expédition
shipping_info = self.shipping_system.schedule_delivery(order_data)
# Envoyer une notification
self.notification_system.send_confirmation(order_data, shipping_info)
return Commande traitée avec succès
class InventorySystem:
def check_stock(self, order_data):
print(Vérification de l'inventaire...)
return True
def update_stock(self, order_data):
print(Mise à jour de l'inventaire...)
class PaymentSystem:
def process_payment(self, order_data):
print(Traitement du paiement...)
return True
class ShippingSystem:
def schedule_delivery(self, order_data):
print(Organisation de l'expédition...)
return Numéro de suivi : SF123456789
class NotificationSystem:
def send_confirmation(self, order_data, shipping_info):
print(fEnvoyer un e-mail de confirmation, informations de livraison : {shipping_info})
# Exemple d'utilisation
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(fRésultat de la commande : {result})
Avantages et inconvénients du patron Médiateur
Avantages
| Avantages | Description |
|---|---|
| Principe de responsabilité unique | Centralise la logique d'interaction entre les composants dans le médiateur. |
| Principe ouvert/fermé | Permet d'introduire de nouveaux médiateurs sans modifier les composants existants. |
| Réduit le couplage | Réduit les dépendances directes entre les composants. |
| Facile à réutiliser | Les composants individuels peuvent être réutilisés plus facilement dans différents contextes. |
Inconvénients
| Inconvénients | Description |
|---|---|
| Le médiateur peut devenir complexe | À mesure que la logique d'interaction augmente, le médiateur peut devenir un objet omniscient. |
| Surcoût de performance | Toute communication passe par le médiateur, ce qui peut introduire un goulot d'étranglement de performance. |
| Difficile à déboguer | La logique d'interaction est concentrée dans le médiateur, donc le débogage peut nécessiter de suivre plusieurs composants. |
Bonnes pratiques et considérations
1. Répartir raisonnablement les responsabilités du médiateur
Ne laissez pas un seul médiateur tout gérer ; vous pouvez répartir plusieurs médiateurs selon les domaines métier :
Exemple
def notify(self, sender, event, data):
if event == "user_registered":
# Gérer la logique après l'inscription de l'utilisateur
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":
# Gérer la logique après la création de la commande
self.validate_order(data)
self.process_payment(data)
self.update_inventory(data)
2. Éviter un médiateur trop complexe
Si le médiateur devient trop complexe, envisagez d'utiliser d'autres motifs ou de refactoriser :
Exemple
class ComplexMediator:
def handle_event(self, event_type, data):
if event_type == "user_action":
# Beaucoup de logique métier complexe...
pass
elif event_type == "system_event":
# Logique plus complexe...
pass
# Bonne pratique - utiliser le patron de stratégie pour décomposer une logique complexe
class EventHandler:
def handle(self, data):
pass
class UserActionHandler(EventHandler):
def handle(self, data):
# Gérer spécifiquement les actions utilisateur
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. Envisagez de combiner avec le patron Observateur
Le patron Médiateur est souvent utilisé en combinaison avec le patron Observateur :
Exemple
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=Aucune):
# Logique du médiateur
event_data = {
"message": message,
"sender": expéditeur,
"receiver": destinataire
}
self.notify_observers("message_sent", event_data)
class LoggingObserver(Observer):
def update(self, event, data):
if event == "message_sent":
print(fLog : {data['sender']} a envoyé un message)
Exercices pratiques
Exercice 1 : Améliorer un système de salon de discussion
Étendre le système de salon de discussion précédent et ajouter les fonctionnalités suivantes :
- Prendre en charge la création de plusieurs salons de discussion
- Prendre en charge le changement d'utilisateurs entre différents salons de discussion.
- Ajouter des fonctions d'administrateur (exclusion d'utilisateurs, mise en sourdine, etc.)
Exercice 2 : Concevoir un système de réservation de billets d'avion
Utilisez le patron Médiateur pour concevoir un système de réservation de billets d'avion, comprenant les composants suivants :
- Recherche de vol
- Sélection des sièges
- Traitement des paiements
- Génération des billets
- Envoi de notifications
Les composants doivent fonctionner en coordination via le médiateur.
Exercice 3 : Système de contrôle de maison intelligente
Concevez un système de contrôle de maison intelligente, comprenant :
- Contrôle de l'éclairage
- Régulation de la température
- Surveillance de sécurité
- Mode scène
Implémenter une liaison intelligente entre appareils via le médiateur.
Résumé
Le patron Médiateur est un outil puissant pour gérer des interactions complexes entre objets. Il simplifie l’architecture système en introduisant un centre de coordination, et convient particulièrement aux scénarios suivants :
- Des relations d’interaction en réseau complexes existent entre les objets
- Lorsqu’il est nécessaire de contrôler de manière centralisée la logique d’interaction entre plusieurs objets
- Espérer réduire le couplage entre les objets
Rappelez-vous, le patron Médiateur n’est pas une solution miracle. Lorsque la logique d’interaction est simple, une communication directe d’objet à objet peut être plus appropriée. Toutefois, face à des systèmes complexes, une utilisation appropriée du patron Médiateur peut considérablement améliorer la maintenabilité et l’extensibilité du code.
Points clés :
- Le médiateur encapsule la logique d’interaction entre les objets
- Les composants communiquent uniquement avec le médiateur et ne dépendent pas directement les uns des autres
- Le médiateur peut devenir complexe et nécessite une conception soignée
- La combinaison avec d’autres patrons (comme Observer) peut donner de meilleurs résultats