> For the complete documentation index, see [llms.txt](https://frogs-organization-2.gitbook.io/oop.-sbornik./llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://frogs-organization-2.gitbook.io/oop.-sbornik./23.-patterny-povedeniya./posrednik-mediator.md).

# Посредник (Mediator)

### Суть паттерна <a href="#intent" id="intent"></a>

**Посредник** — это поведенческий паттерн проектирования, который позволяет уменьшить связанность множества классов между собой, благодаря перемещению этих связей в один класс-посредник.

<figure><img src="https://refactoringguru.cn/images/patterns/content/mediator/mediator.png?id=0264bd857a231b6ea2d0c537c092e698" alt="Паттерн Посредник" width="640"><figcaption></figcaption></figure>

### &#x20;Проблема <a href="#problem" id="problem"></a>

Предположим, что у вас есть диалог создания профиля пользователя. Он состоит из всевозможных элементов управления — текстовых полей, чекбоксов, кнопок.

<figure><img src="https://refactoringguru.cn/images/patterns/diagrams/mediator/problem1-en.png?id=86f99055b3e60bb8834dcc7222922bdf" alt="Беспорядочные связи между элементами пользовательского интерфейса" width="600"><figcaption><p>Беспорядочные связи между элементами пользовательского интерфейса.</p></figcaption></figure>

Отдельные элементы диалога должны взаимодействовать друг с другом. Так, например, чекбокс «у меня есть собака» открывает скрытое поле для ввода имени домашнего любимца, а клик по кнопке отправки запускает проверку значений всех полей формы.

<figure><img src="https://refactoringguru.cn/images/patterns/diagrams/mediator/problem2.png?id=072c51eee4dd90c0972866440c87c1c3" alt="Код элементов раздут условиями, которые часто меняются" width="360"><figcaption><p>Код элементов нужно трогать при изменении каждого диалога.</p></figcaption></figure>

Прописав эту логику прямо в коде элементов управления, вы поставите крест на их повторном использовании в других местах приложения. Они станут слишком тесно связанными с элементами диалога редактирования профиля, которые не нужны в других контекстах. Поэтому вы сможете использовать либо все элементы сразу, либо ни одного.

### &#x20;Решение <a href="#solution" id="solution"></a>

Паттерн Посредник заставляет объекты общаться не напрямую друг с другом, а через отдельный объект-посредник, который знает, кому нужно перенаправить тот или иной запрос. Благодаря этому, компоненты системы будут зависеть только от посредника, а не от десятков других компонентов.

В нашем примере посредником мог бы стать диалог. Скорее всего, класс диалога и так знает, из каких элементов состоит, поэтому никаких новых связей добавлять в него не придётся.

<figure><img src="https://refactoringguru.cn/images/patterns/diagrams/mediator/solution1-en.png?id=dd991a5b7830de8d43f82b084e021713" alt="Элементы общаются через посредника" width="600"><figcaption><p>Элементы интерфейса общаются через посредника.</p></figcaption></figure>

Основные изменения произойдут внутри отдельных элементов диалога. Если раньше при получении клика от пользователя объект кнопки сам проверял значения полей диалога, то теперь его единственной обязанностью будет сообщить диалогу о том, что произошёл клик. Получив извещение, диалог выполнит все необходимые проверки полей. Таким образом, вместо нескольких зависимостей от остальных элементов кнопка получит только одну — от самого диалога.

Чтобы сделать код ещё более гибким, можно выделить общий интерфейс для всех посредников, то есть диалогов программы. Наша кнопка станет зависимой не от конкретного диалога создания пользователя, а от абстрактного, что позволит использовать её и в других диалогах.

Таким образом, посредник скрывает в себе все сложные связи и зависимости между классами отдельных компонентов программы. А чем меньше связей имеют классы, тем проще их изменять, расширять и повторно использовать.

### &#x20;Аналогия из жизни <a href="#analogy" id="analogy"></a>

<figure><img src="https://refactoringguru.cn/images/patterns/diagrams/mediator/live-example.png?id=aa1de3cb7b63aa623e63578a1e43399a" alt="Пример с диспетчерской башней." width="370"><figcaption><p>Пилоты самолётов общаются не напрямую, а через диспетчера.</p></figcaption></figure>

Пилоты садящихся или улетающих самолётов не общаются напрямую с другими пилотами. Вместо этого они связываются с диспетчером, который координирует действия нескольких самолётов одновременно. Без диспетчера пилотам приходилось бы все время быть начеку и следить за всеми окружающими самолётами самостоятельно, а это приводило бы к частым катастрофам в небе.

Важно понимать, что диспетчер не нужен во время всего полёта. Он задействован только в зоне аэропорта, когда нужно координировать взаимодействие многих самолётов.

### &#x20;Структура <a href="#structure" id="structure"></a>

<figure><img src="https://refactoringguru.cn/images/patterns/diagrams/mediator/structure.png?id=1f2accc7820ecfe9665b6d30cbc0bc61" alt="Структура классов паттерна Посредник" width="520"><figcaption></figcaption></figure>

1. **Компоненты** — это разнородные объекты, содержащие бизнес-логику программы. Каждый компонент хранит ссылку на объект посредника, но работает с ним только через абстрактный интерфейс посредников. Благодаря этому, компоненты можно повторно использовать в другой программе, связав их с посредником другого типа.
2. **Посредник** определяет интерфейс для обмена информацией с компонентами. Обычно хватает одного метода, чтобы оповещать посредника о событиях, произошедших в компонентах. В параметрах этого метода можно передавать детали события: ссылку на компонент, в котором оно произошло, и любые другие данные.
3. **Конкретный посредник** содержит код взаимодействия нескольких компонентов между собой. Зачастую этот объект не только хранит ссылки на все свои компоненты, но и сам их создаёт, управляя дальнейшим жизненным циклом.
4. Компоненты не должны общаться друг с другом напрямую. Если в компоненте происходит важное событие, он должен оповестить своего посредника, а тот сам решит — касается ли событие других компонентов, и стоит ли их оповещать. При этом компонент-отправитель не знает кто обработает его запрос, а компонент-получатель не знает кто его прислал.

### Применимость <a href="#applicability" id="applicability"></a>

&#x20;Когда вам сложно менять некоторые классы из-за того, что они имеют множество хаотичных связей с другими классами.

&#x20;Посредник позволяет поместить все эти связи в один класс, после чего вам будет легче их отрефакторить, сделать более понятными и гибкими.

&#x20;Когда вы не можете повторно использовать класс, поскольку он зависит от уймы других классов.

&#x20;После применения паттерна компоненты теряют прежние связи с другими компонентами, а всё их общение происходит косвенно, через объект-посредник.

&#x20;Когда вам приходится создавать множество подклассов компонентов, чтобы использовать одни и те же компоненты в разных контекстах.

&#x20;Если раньше изменение отношений в одном компоненте могли повлечь за собой лавину изменений во всех остальных компонентах, то теперь вам достаточно создать подкласс посредника и поменять в нём связи между компонентами.

### &#x20;Шаги реализации <a href="#checklist" id="checklist"></a>

1. Найдите группу тесно переплетённых классов, отвязав которые друг от друга, можно получить некоторую пользу. Например, чтобы повторно использовать их код в другой программе.
2. Создайте общий интерфейс посредников и опишите в нём методы для взаимодействия с компонентами. В простейшем случае достаточно одного метода для получения оповещений от компонентов.

   Этот интерфейс необходим, если вы хотите повторно использовать классы компонентов для других задач. В этом случае всё, что нужно сделать — это создать новый класс конкретного посредника.
3. Реализуйте этот интерфейс в классе конкретного посредника. Поместите в него поля, которые будут содержать ссылки на все объекты компонентов.
4. Вы можете пойти дальше и переместить код создания компонентов в класс посредника, после чего он может напоминать фабрику или фасад.
5. Компоненты тоже должны иметь ссылку на объект посредника. Связь между ними удобнее всего установить, подавая посредника в параметры конструктора компонентов.
6. Измените код компонентов так, чтобы они вызывали метод оповещения посредника, вместо методов других компонентов. С противоположной стороны, посредник должен вызывать методы нужного компонента, когда получает оповещение от компонента.

### &#x20;Преимущества и недостатки <a href="#pros-cons" id="pros-cons"></a>

* &#x20;Устраняет зависимости между компонентами, позволяя повторно их использовать.
* &#x20;Упрощает взаимодействие между компонентами.
* &#x20;Централизует управление в одном месте.
* &#x20;Посредник может сильно раздуться.

## Пример

<pre class="language-cpp"><code class="lang-cpp"># include &#x3C;iostream>
# include &#x3C;memory>
# include &#x3C;list>
# include &#x3C;vector>

using namespace std;

class Message {};         // Request

class Mediator;

class Colleague
{
private:
        weak_ptr&#x3C;Mediator> mediator;

public:
        virtual ~Colleague() = default;
        
        void setMediator(shared_ptr&#x3C;Mediator> mdr) { mediator = mdr; }
        
        virtual bool send(shared_ptr&#x3C;Message> msg);
        virtual void receive(shared_ptr&#x3C;Message> msg) = 0;
};

class ColleagueLeft : public Colleague
{
public:
        void receive(shared_ptr&#x3C;Message> msg) override { cout &#x3C;&#x3C; "Right - > Left;" &#x3C;&#x3C; endl; }
};

class ColleagueRight : public Colleague
{
public:
        void receive(shared_ptr&#x3C;Message> msg) override { cout &#x3C;&#x3C; "Left - > Right;" &#x3C;&#x3C; endl; }
};

class Mediator
{
protected:
        list&#x3C;shared_ptr&#x3C;Colleague>> colleagues;

public:
        virtual ~Mediator() = default;
        
        virtual bool send(const Colleague* coleague, shared_ptr&#x3C;Message> msg) = 0;
        
        static bool add(shared_ptr&#x3C;Mediator> mediator, initializer_list&#x3C;shared_ptr&#x3C;Colleague>> list);
};

class ConMediator : public Mediator
{
public:
        bool send(const Colleague* coleague, shared_ptr&#x3C;Message> msg) override;
};

# pragma region Methods Colleague
bool Colleague::send(shared_ptr&#x3C;Message> msg)
{
        shared_ptr&#x3C;Mediator> mdr = mediator.lock();

        return mdr ? mdr->send(this, msg) : false;
}
# pragma endregion

# pragma region Methods Mediator
bool Mediator::add(shared_ptr&#x3C;Mediator> mediator, initializer_list&#x3C;shared_ptr&#x3C;Colleague>> list)
{
        if (!mediator || list.size() == 0) return false;

        for (auto elem : list)
        {
                mediator->colleagues.push_back(elem);
                elem->setMediator(mediator);
        }

        return true;
}

bool ConMediator::send(const Colleague* colleague, shared_ptr&#x3C;Message> msg)
{
        bool flag = false;
        for (auto&#x26;&#x26; elem : colleagues)
        {
                if (dynamic_cast&#x3C;const ColleagueLeft*>(colleague) &#x26;&#x26; dynamic_cast&#x3C;ColleagueRight*>(elem.get()))
                {
                        elem->receive(msg);
                        flag = true;
                }
<strong>                else if (dynamic_cast&#x3C;const ColleagueRight*>(colleague) &#x26;&#x26; dynamic_cast&#x3C;ColleagueLeft*>(elem.get()))
</strong>                {
                        elem->receive(msg);
                        flag = true;
                }
        }

        return flag;
}
#pragma endregion

int main()
{
        shared_ptr&#x3C;Mediator> mediator = make_shared&#x3C;ConMediator>();
        
        shared_ptr&#x3C;Colleague> col1 = make_shared&#x3C;ColleagueLeft>();
        shared_ptr&#x3C;Colleague> col2 = make_shared&#x3C;ColleagueRight>();
        shared_ptr&#x3C;Colleague> col3 = make_shared&#x3C;ColleagueLeft>();
        shared_ptr&#x3C;Colleague> col4 = make_shared&#x3C;ColleagueLeft>();
        
        Mediator::add(mediator, { col1, col2, col3, col4 });
        
        shared_ptr&#x3C;Message> msg = make_shared&#x3C;Message>();
        
        col1->send(msg);
        col2->send(msg);
}

</code></pre>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://frogs-organization-2.gitbook.io/oop.-sbornik./23.-patterny-povedeniya./posrednik-mediator.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
