Паттерн «Посредник» в Python
Паттерн Посредник — это поведенческий шаблон проектирования, который уменьшает зависимости прямого взаимодействия между объектами, вводя объект-посредник. Представьте реальную диспетчерскую вышку аэропорта — все самолёты не общаются друг с другом напрямую, а координируют взлёты, посадки и маршруты через диспетчерскую вышку, таким образом избегая хаоса и конфликтов.
В разработке программного обеспечения, когда между несколькими объектами существуют сложные взаимодействия, паттерн Посредник может инкапсулировать эти взаимодействия в посреднике, тем самым уменьшая связанность между объектами.
Ключевая идея
Основная идея паттерна Посредник —"Не общайтесь напрямую; координируйте через посредника."Он решает сложность взаимодействий «многие ко многим» между объектами, делая систему проще в поддержке и расширении.
Зачем нужен паттерн Посредник?
Сценарий проблемы
Предположим, мы разрабатываем систему чата с несколькими пользователями, которым нужно отправлять сообщения друг другу. Без посредника код может выглядеть так:
Пример
def __init__(self, name):
self.name = name
self.other_users = [] # Нужно знать всех остальных пользователей
def send_message(self, message, to_user):
print(f"{self.name} отправил пользователю {to_user.name}: {message}")
to_user.receive_message(message, self)
def receive_message(self, message, from_user):
print(f{self.name} получил сообщение от {from_user.name}: {message})
# Пример использования
user1 = User(Сяомин)
user2 = User(Сяохун)
user3 = User(Сяоган)
# Каждому пользователю нужно знать всех остальных пользователей
user1.other_users = [user2, user3]
user2.other_users = [user1, user3]
user3.other_users = [user1, user2]
user1.send_message(Привет!, user2)
Недостатки этого дизайна:
- Высокая связанность между объектами; каждый пользователь должен знать всех остальных пользователей.
- Добавление нового пользователя требует изменения всех существующих пользователей.
- Сложно поддерживать; логика взаимодействия разбросана по объектам.
Решение
Паттерн «Посредник» решает эту проблему, вводя чат-комнату (посредника):
Пример
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=Отсутствует):
if to_user_name: # Личный чат
if to_user_name in self.users:
self.users[to_user_name].receive_message(message, from_user)
else: # Групповой чат
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} получил сообщение от {from_user.name}: {message})
# Пример использования
chat_room = ChatRoom()
user1 = User(Сяомин)
user2 = User(Сяохун)
user3 = User(Сяоган)
chat_room.register_user(user1)
chat_room.register_user(user2)
chat_room.register_user(user3)
user1.send_message(Привет всем!) # Широковещательное сообщение
user2.send_message(Привет, Xiaoming!, Xiaoming) # Личный чат
Реализация паттерна Посредник
Базовая структура
Давайте рассмотрим реализацию паттерна Посредник на более полном примере:
Пример
from typing import List
# Абстрактный посредник
class Mediator(ABC):
@abstractmethod
def notify(self, sender: object, event: str, data: dict = None):
pass
# Конкретный посредник - Чат-комната
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=Отсутствует):
if event == "send_message":
message = data.get("message")
target_user = data.get("target_user")
if target_user: # Приватный чат
for user in self.users:
if user.name == target_user:
user.receive_message(message, sender.name)
else: # Групповой чат
for user in self.users:
if user != sender:
user.receive_message(message, sender.name)
elif event == "user_joined":
message = fСистема: {sender.name} присоединился к чату
for user in self.users:
if user != sender:
user.receive_message(message, System)
# Базовый класс компонента
class BaseComponent:
def __init__(self):
self._mediator = None
def set_mediator(self, mediator: Mediator):
self._mediator = mediator
# Конкретный компонент - 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} отправил(а) сообщение: {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} получил(а) сообщение от {from_name}: {message})
# Пример использования
def main():
# Создать посредника
chat_room = ChatRoomMediator()
# Создать пользователя
alice = User("Алиса")
bob = User("Боб")
charlie = User("Чарли")
# Зарегистрировать пользователей в чате
chat_room.add_user(alice)
chat_room.add_user(bob)
chat_room.add_user(charlie)
print(=== Демонстрация чата ===)
# Алиса присоединяется к чату
alice.join_chat()
# Отправить сообщение
alice.send_message(Привет всем, я Алиса!)
bob.send_message(Добро пожаловать, Alice!)
charlie.send_message(Привет, Alice!, "Alice") # Личный чат
# Bob отправляет групповое сообщение
bob.send_message(Кто-нибудь онлайн?)
if __name__ == "__main__":
main()
Вывод
=== 聊天室演示 === 系统: 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 的消息: 有人在线吗?
UML-структура паттерна «Посредник»

Описание структуры
- Mediator (интерфейс посредника): определяет интерфейс взаимодействия
- ConcreteMediator (конкретный посредник): реализует логику координации и знает все компоненты
- BaseComponent (базовый компонент): содержит ссылку на посредника
- ConcreteComponent (конкретный компонент): реализует конкретную бизнес-логику
Реальные сценарии применения
Сценарий 1: GUI-приложение
В графическом пользовательском интерфейсе различные элементы управления (кнопки, текстовые поля, флажки и т. д.) координируют взаимодействие через посредника:
Пример
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":
# Когда введены имя пользователя или пароль, проверьте, можно ли включить кнопку входа
username = self.username_input.get_text()
password = self.password_input.get_text()
self.login_button.set_enabled(bool(username and пароль))
elif event == "login_clicked":
# Логика обработки входа
username = self.username_input.get_text()
password = self.password_input.get_text()
remember = self.remember_checkbox.is_checked()
print(fЛогин: {username}, Запомнить меня: {remember})
elif event == "remember_changed":
print(Опция «Запомнить меня» изменена)
class UIComponent:
def __init__(self, mediator=None):
self.mediator = mediator
def set_mediator(self, mediator):
self.mediator = mediator
class Button(UI):
def click(self):
if self.mediator:
self.mediator.notify(self, "login_clicked")
def set_enabled(self, enabled):
print(fButton {'Включить' if enabled else 'Отключить'})
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
# Пример использования
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)
# Имитировать ввод пользователя
username_input.set_text("user123")
password_input.set_text("pass123")
login_btn.click()
Сценарий 2: Система заказов электронной коммерции
В системе электронной коммерции обработка заказов включает несколько подсистем, таких как управление запасами, платежи и логистика:
Пример
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(=== Начало обработки заказа ===)
# Проверить запасы
if not self.inventory_system.check_stock(order_data):
return Недостаточно запасов
# Обработать платеж
payment_result = self.payment_system.process_payment(order_data)
if not payment_result:
return Платёж не прошёл
# Обновить инвентарь
self.inventory_system.update_stock(order_data)
# Организовать доставку
shipping_info = self.shipping_system.schedule_delivery(order_data)
# Отправить уведомление
self.notification_system.send_confirmation(order_data, shipping_info)
return Заказ успешно обработан
class InventorySystem:
def check_stock(self, order_data):
print(Проверка запасов...)
return True
def update_stock(self, order_data):
print(Обновление запасов...)
class PaymentSystem:
def process_payment(self, order_data):
print(Обработка платежа...)
return True
class ShippingSystem:
def schedule_delivery(self, order_data):
print(Организация отправки...)
return Номер отслеживания: SF123456789
class NotificationSystem:
def send_confirmation(self, order_data, shipping_info):
print(fОтправка письма с подтверждением, информация о доставке: {shipping_info})
# Пример использования
mediator = OrderMediator()
mediator.inventory_system = InventorySystem()
mediator.payment_system = PaymentSystem()
mediator.shipping_system = ShippingSystem()
mediator.notification_system = Notification()
order_data = {"product_id": "123", "quantity": 2, "user_id": "user001"}
result = mediator.place_order(order_data)
print(fРезультат заказа: {result})
Преимущества и недостатки паттерна «Посредник»
Преимущества
| Преимущества | Описание |
|---|---|
| Принцип единственной ответственности | Централизует логику взаимодействия между компонентами в посреднике. |
| Принцип открытости/закрытости | Позволяет добавлять новых посредников без изменения существующих компонентов. |
| Снижает связанность | Уменьшает прямые зависимости между компонентами. |
| Легко повторно использовать | Отдельные компоненты можно легче повторно использовать в разных контекстах. |
Недостатки
| Недостатки | Описание |
|---|---|
| Посредник может стать сложным | По мере роста логики взаимодействия посредник может превратиться в божественный объект. |
| Накладные расходы на производительность | Вся коммуникация проходит через посредника, что может создать узкое место по производительности. |
| Сложно отлаживать | Логика взаимодействия сосредоточена в посреднике, поэтому отладка может потребовать отслеживания множества компонентов. |
Лучшие практики и рекомендации
1. Разумно разделяйте обязанности посредника
Не позволяйте одному медиатору обрабатывать всё; можно разделить несколько медиаторов по бизнес-доменам:
Пример
def notify(self, sender, event, data):
if event == "user_registered":
# Обработка логики после регистрации пользователя
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":
# Обработка логики после создания заказа
self.validate_order(data)
self.process_payment(data)
self.update_inventory(data)
2. Избегайте чрезмерно сложного посредника
Если посредник становится слишком сложным, рассмотрите возможность использования других паттернов или рефакторинга:
Пример
class ComplexMediator:
def handle_event(self, event_type, data):
if event_type == "user_action":
# Много сложной бизнес-логики...
pass
elif event_type == "system_event":
# Ещё более сложная логика...
pass
# Хорошая практика — используйте паттерн «Стратегия» для декомпозиции сложной логики
class EventHandler:
def handle(self, data):
pass
class UserActionHandler(EventHandler):
def handle(self, data):
# Специально обрабатывать действия пользователя
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. Рассмотрите объединение с паттерном «Наблюдатель»
Паттерн «Посредник» часто используется в сочетании с паттерном «Наблюдатель»:
Пример
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=Отсутствует):
# Логика посредника
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Лог: {data['sender']} отправил сообщение)
Практические упражнения
Упражнение 1: Улучшение системы чата
Расширьте предыдущую систему чата и добавьте следующие возможности:
- Поддержка создания нескольких комнат чата
- Поддержка переключения пользователей между разными комнатами чата.
- Добавьте функции администратора (исключение пользователей, заглушение и т. д.)
Упражнение 2: Проектирование системы бронирования авиабилетов
Используйте паттерн «Посредник» для проектирования системы бронирования авиабилетов, включающей следующие компоненты:
- Поиск рейсов
- Выбор места
- Обработка платежей
- Генерация билета
- Отправка уведомлений
Компоненты должны работать согласованно через посредника.
Упражнение 3: Система управления умным домом
Спроектируйте систему управления умным домом, включающую:
- Управление освещением
- Регулирование температуры
- Мониторинг безопасности
- Режим сцены
Реализуйте интеллектуальное взаимодействие между устройствами через посредника.
Итог
Паттерн Посредник — мощный инструмент для обработки сложных взаимодействий объектов. Он упрощает архитектуру системы, вводя координационный центр, и особенно подходит для следующих сценариев:
- Между объектами существуют сложные сетевые взаимодействия
- Когда необходимо централизованно управлять логикой взаимодействия между несколькими объектами
- Стремление снизить связанность между объектами
Помните, паттерн Посредник — не серебряная пуля. Когда логика взаимодействия проста, прямое взаимодействие между объектами может быть более уместным. Однако при работе со сложными системами правильное использование паттерна Посредник может значительно повысить сопровождаемость и расширяемость кода.
Ключевые моменты:
- Посредник инкапсулирует логику взаимодействия между объектами
- Компоненты взаимодействуют только с посредником и не зависят напрямую друг от друга
- Посредник может стать сложным и требует тщательного проектирования
- Сочетание с другими паттернами (например, Наблюдатель) может дать лучшие результаты