Паттерн Mediator (Dispatcher)

Теперь настало время разобрать что же такое Mediator. Его отличие, а также преимущества относительно Observer’а я рассмотрю в отдельной сравнительной статье. Но на самом деле, мне кажется многие догадаются об этом и сами, когда появится ясность и понимание данного паттерна.

Mediator, Dispatcher

Лично у меня долгое время была путаница в голове, когда я читал и разбирал различные Dispatcher’ы и Mediator’ы. Я никак не мог понять в чем их разница. Так вот на самом деле как оказалось это одно и то же, просто названо разными словами.

Медиатор/Диспетчер - это посредник, который нужен, чтобы убрать ненужные связи между различными объектами.

Аналогия

Если мы взглянем на реальный мир, то больше всего мне нравится аналогия с телефонной станцией. Допустим в двух соседних домах у жильцов возникла необходимость иметь возможность общаться друг с другом по телефону. И вот они начали протягивать провода друг к другу. Вася больше всего дружит с Мишей, поэтому этот провод он протягивает в первую очередь. В принципе, он не против время от времени звонить своей подруге Гале. Тянем шнур и к ней. Ну и на всякий случай пусть будет связь с его бывшей подружкой Мариной, которая имеет привычку раз в год ему звонить и тешить его самолюбие рассказами о том, что она так и не нашла себе никого лучше Васи. И вот таким образом у Васи дома торчат три провода. Но это в лучшем случае.

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

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

Ближе к сути

Суть медиатора сводится к тому, что он является промежуточным звеном в любой системе. Таким образом, он позволяет избежать прямой связанности компонентов между собой. Компоненты в свою очередь связаны только с медиатором (диспетчером) и эта их единственная возможность общения с внешним миром (имеется в виду с объектами вне компонента).

Реализация

Снова я оговорюсь, что представленный ниже код - иллюстрация подхода. Потому я нарочно не захламляю его лишними методами, проверками и прочей логикой. Наша цель - понять суть.

Саша, Маша и новости

Если вы читали статью про паттерн Observer, то вы уже знаете наших героев. Предлагаю реализовать следующий пример.

Существует некий новостной журнал. Его задача генерировать новости и зарабатывать на этом деньги. Для этого у журнала есть инструменты для добавления новостей. Они даже наняли редактора новостей, который умеет эти новости генерировать, но он фрилансер и вообще живет в другом часовом поясе. Как быстро распространять новости, журнал пока еще не представляет. Но им повезло, есть агентство, которое умеет набирать базу подписчиков и отправлять им то, что подписчикам интересно. Ну вот юзкейс для реализации и готов. Давайте разберем кто у нас есть:

  • Саша и Маша - подписчики
  • Новостной журнал - хранилище новостей с инструментом для добавления новости
  • Редактор новостей - сторонний компонент, который добавляет новости
  • Агентство - диспетчер/медиатор

Вначале мы просим редактора заполнить базу журнала какими-нибудь шокирующими новостями. Затем запустим режим “лайв”, в котором каждые 3 секунды журнал будет публиковать по новости. Если новости закончатся - мы попросим диспетчера сообщить об этом редактору, который быстренько подсуетится и придумает новость.

Для Саши и Маши это все будет происходит “за кулисами”. Они не хотят вникать в суть всего механизма, а просто хотят читать новости. Маша про моду, Саша все остальное. Как мы видим диспетчер обо всем позаботится, и свяжет несколько компонентов в единый работающий механизм.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//Опишем наш медиатор. Он будет диспетчером рассылки новостей.
var Dispatcher = {
//хранилище всех подписчиков
subscribers: {},

//метод для подписки на какое-либо событие
on: function (event, handler) {
if (this.subscribers.hasOwnProperty(event)) {
this.subscribers.push(handler);
} else {
this.subscribers[event] = [handler];
}
},

//метод для вызова обработчиков на некое событие
trigger: function (event, data) {
for (var ev in this.subscribers) {
if (ev !== event) {
continue;
}
if (this.subscribers.hasOwnProperty(ev)) {
this.subscribers[ev].forEach(function (handler) {
handler(data);
});
}
}
}
};

//Наш журнал, с некоторыми новостями
var Magazine = {
news: [],

//Метод в журнале, который просто добавляет новую новость
addNews: function(category, title){
this.news.push({
category: category,
title: title
});
},

//Режим "лайв". Каждые 3 секунды выпускаем новость в свет
live: function () {
var newsItem;


setInterval(function () {
if (this.news.length === 0) {
//если новости закончились - кричим об этом
Dispatcher.trigger('No news');
return;
}
newsItem = this.news.splice(0, 1)[0];
//отдаем диспетчеру очередную новость
Dispatcher.trigger(newsItem.category, newsItem.title);
}.bind(this), 3000);
}
};

//Саша, который всем обо всем рассказывает
var Sasha = {
tellToEveryone: function (anything) {
console.log('I am Sasha. Did you heard that ' + anything + ' ?');
}
};

//Маша, которая обо всем пишет в блог
var Masha = {
writeToBlog: function (anything) {
console.log("Hi, it's Masha. Just known that " + anything + '!!! OMG!');
}
};

//Редактор новостей, ответственный за генерирование новостей
var NewsMaker = {
generateNews: function(){
Magazine.addNews('Sport', 'Auto news was generated #' + (Math.random(10)*100).toFixed(0));
},
addNews: function(category, title){
Magazine.addNews(category, title);
}
};

//Сообщаем диспетчеру о том, на что подписан каждый из наших участников
Dispatcher.on('Sport', Sasha.tellToEveryone);
Dispatcher.on('News', Sasha.tellToEveryone);
Dispatcher.on('Fashion', Masha.writeToBlog);
Dispatcher.on('No news', NewsMaker.generateNews);

//пусть редактор добавит несколько новостей сразу
NewsMaker.addNews('Sport',"Alenichev's Spartak won his first home match against Rubin Kazan");
NewsMaker.addNews('Fashion','The best shop ever opened in Moscow');
NewsMaker.addNews('Sport','Messi signed new Barcelona contract');
NewsMaker.addNews('News','Martians have landed on Earth');
NewsMaker.addNews('Fashion','Do your selfie right!');
NewsMaker.addNews('Sport','Christiano Ronaldo moved to Anzhi');
NewsMaker.addNews('News','Crimea is ours!');

//Запускаем режим "лайв"
Magazine.live();

Поиграться с примером можно традиционно в JSFiddle. Не забудьте открыть консоль.

Надеюсь, статья помогла вам разобраться в том что же такое медиатор и зачем он нужен.