Что такое паттерн Медиатор (Mediator)?

«Что такое паттерн Медиатор (Mediator)?» — вопрос из категории Паттерны, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Медиатор (Mediator) — это поведенческий паттерн проектирования, который уменьшает прямые связи между множеством объектов, заставляя их взаимодействовать не напрямую, а через специальный объект-посредник. Этот посредник централизует сложную логику взаимодействия, делая систему более управляемой и упрощая изменение отдельных компонентов.

Проблема, которую он решает: Представьте группу компонентов UI (кнопки, поля ввода, чекбоксы), которые должны влиять друг на друга. Прямые ссылки между ними создают «паутину зависимостей», где изменение одного компонента влечёт за собой правки в нескольких других. Это делает код хрупким и сложным для понимания.

Решение: Медиатор берёт на себя роль координатора. Каждый компонент знает только о медиаторе, а не о других компонентах. Когда компоненту нужно что-то сообщить системе, он отправляет событие медиатору, который решает, как на это событие должны отреагировать другие компоненты.

Преимущества:

  • Уменьшение связанности: Компоненты становятся независимыми друг от друга.
  • Централизация управления: Вся логика взаимодействия находится в одном месте, что упрощает её анализ и изменение.
  • Упрощение повторного использования компонентов: Компоненты легче выносить в другие контексты, так как они не зависят от конкретных «коллег».

Недостатки:

  • Риск создания «Богатого объекта»: Медиатор может превратиться в монолитный класс, который знает и делает слишком много, нарушая принцип единственной ответственности.

Пример на C# (Упрощённая система диалога в чате):

// 1. Интерфейс Медиатора
public interface IChatMediator
{
    void SendMessage(string message, User sender);
    void RegisterUser(User user);
}

// 2. Конкретный Медиатор
public class ChatRoom : IChatMediator
{
    private List<User> _users = new List<User>();

    public void RegisterUser(User user)
    {
        _users.Add(user);
        user.Mediator = this; // Устанавливаем связь «пользователь -> медиатор»
    }

    public void SendMessage(string message, User sender)
    {
        // Медиатор решает, как обработать сообщение.
        // Здесь он рассылает его всем, кроме отправителя.
        foreach (var user in _users)
        {
            if (user != sender)
            {
                user.Receive(message, sender.Name);
            }
        }
        // Логику можно легко изменить: отправлять только адресату, фильтровать слова и т.д.
    }
}

// 3. Класс «Коллеги» (Colleague), который знает только о Медиаторе
public class User
{
    public string Name { get; }
    public IChatMediator Mediator { get; set; } // Ссылка на посредника

    public User(string name)
    {
        Name = name;
    }

    public void Send(string message)
    {
        Console.WriteLine($"{Name} отправляет: {message}");
        // Вместо прямого вызова других пользователей, сообщение отправляется медиатору.
        Mediator?.SendMessage(message, this);
    }

    public void Receive(string message, string senderName)
    {
        Console.WriteLine($"[{Name}] получил от {senderName}: {message}");
    }
}

// 4. Клиентский код
class Program
{
    static void Main()
    {
        // Создаём медиатор (чат-комнату)
        IChatMediator chatRoom = new ChatRoom();

        // Создаём пользователей
        User alice = new User("Alice");
        User bob = new User("Bob");
        User charlie = new User("Charlie");

        // Регистрируем пользователей в медиаторе
        chatRoom.RegisterUser(alice);
        chatRoom.RegisterUser(bob);
        chatRoom.RegisterUser(charlie);

        // Пользователи общаются через медиатор
        alice.Send("Привет всем!");
        // Вывод:
        // Alice отправляет: Привет всем!
        // [Bob] получил от Alice: Привет всем!
        // [Charlie] получил от Alice: Привет всем!

        bob.Send("Привет, Alice!");
        // Вывод:
        // Bob отправляет: Привет, Alice!
        // [Alice] получил от Bob: Привет, Alice!
        // [Charlie] получил от Bob: Привет, Alice!
    }
}