Коротка відповідь
middleMediator — це паттерн, що централізує взаємодію між об’єктами через один об’єкт‑медіатор, зменшуючи залежності між ними. У TS це виглядає як інтерфейс `IMediator` з методом `notify(sender, event)`, а компоненти реєструються через `register(component)` і отримують посилання на медіатора. Приклад: `class ChatRoom implements IMediator { notify(sender, event) { … } }` і компоненти `User`, `MessageBox` підписуються на `ChatRoom`.
Повне пояснення
1. Що це і навіщо
Mediator — паттерн поведінки, що зменшує кількість прямих зв’язків між об’єктами, централізуючи їх взаємодію через один медіатор. Це знижує складність, полегшує підтримку і розширення системи.
2. Принцип роботи / алгоритм
Компоненти (колонки, кнопки, модулі) не звертаються один до одного безпосередньо. Кожен з них реєструється у медіаторі і отримує посилання на нього. Коли компонент виконує дію, він повідомляє медіатору (notify(sender, event)), а медіатор вирішує, які інші компоненти мають реагувати. Діаграма: Component → Mediator ← Component.
3. Реалізація (JS / TS)
- Базова реалізація
interface IMediator { notify(sender: Component, event: string): void; }
class Mediator implements IMediator {
private components = new Set<Component>();
register(c: Component) { this.components.add(c); c.setMediator(this); }
notify(sender: Component, event: string) {
this.components.forEach(c => c !== sender && c.handle(event));
}
}
class Component {
protected mediator!: IMediator;
setMediator(m: IMediator) { this.mediator = m; }
handle(event: string) {}
}
- Реалістичний приклад – чат‑кімната
class ChatRoom implements IMediator {
private users = new Set<User>();
register(u: User) { this.users.add(u); u.setMediator(this); }
notify(sender: User, msg: string) {
this.users.forEach(u => u !== sender && u.receive(msg));
}
}
class User extends Component {
send(msg: string) { this.mediator.notify(this, msg); }
receive(msg: string) { console.log(`User ${this.id} received: ${msg}`); }
}
- Спрощений для новачка – кнопки, що взаємодіють
class Button extends Component {
click() { this.mediator.notify(this, 'click'); }
}
class Logger extends Component {
handle(event: string) { if (event === 'click') console.log('Button clicked'); }
}
4. Тестування (Jest або Vitest)
test('Mediator forwards event to other components', () => {
const mediator = new Mediator();
const compA = { setMediator: jest.fn(), handle: jest.fn() } as any;
const compB = { setMediator: jest.fn(), handle: jest.fn() } as any;
mediator.register(compA);
mediator.register(compB);
mediator.notify(compA, 'test');
expect(compB.handle).toHaveBeenCalledWith('test');
});
5. Проблеми та edge cases
- Неправильне використання ролей – компонент, що не реєструється у медіаторі, не отримає повідомлення.
- Зайва абстракція – надмірно складний медіатор, що містить логіку всіх компонентів.
- Cyclical dependencies – медіатор сам реєструється у компоненті, що створює цикл.
- Плутанина між паттернами – іноді можна замінити Mediator на Observer, якщо потрібна лише підписка.
- Performance‑пастки – при великій кількості компонентів медіатор може стати вузьким місцем, оскільки викликає
handleу всіх.
6. Оптимізація та продуктивність
Медіатор зменшує кількість прямих зв’язків, що полегшує масштабування. Однак у випадку великої кількості компонентів треба обмежити поширення повідомлень (наприклад, фільтрувати за типом). Використання слабких посилань або кешування результатів може зменшити накладні витрати.
7. Без JS / архітектурні альтернативи
У веб‑інтерфейсі можна реалізувати медіатор через глобальний EventBus (наприклад, mitt), або через контекст у React (React.Context). У чистій архітектурі це може бути окремий сервіс, що обробляє команди і розповсюджує їх між доменними модулями.
8. Cheatsheet + follow-up
- Медіатор:
notify(sender, event)→ розповсюджує повідомлення. - Компонент: реєструється у медіаторі, викликає
notify. - Питання на інтерв’ю: 1) Як медіатор допомагає зменшити залежності? 2) Які ризики пов’язані з надмірною абстракцією медіатора?