Все дело в слабой взаимосвязи и единственной ответственности, которые идут рука об руку с шаблонами MV * (MVC / MVP / MVVM) в JavaScript, которые стали очень современными в последние несколько лет.
Слабая связь - это объектно-ориентированный принцип, в котором каждый компонент системы знает свою ответственность и не заботится о других компонентах (или, по крайней мере, старается не заботиться о них в максимально возможной степени). Слабая связь - это хорошо, потому что вы можете легко повторно использовать разные модули. Вы не связаны с интерфейсами других модулей. Используя публикацию / подписку, вы связаны только с интерфейсом публикации / подписки, что не имеет большого значения - всего два метода. Поэтому, если вы решите повторно использовать модуль в другом проекте, вы можете просто скопировать и вставить его, и он, вероятно, сработает или, по крайней мере, вам не потребуется много усилий, чтобы заставить его работать.
Говоря о слабой связи, следует упомянуть разделение проблем.. Если вы создаете приложение с использованием архитектурного шаблона MV *, у вас всегда есть Модель (и) и Представление (и). Модель - это бизнес-часть приложения. Вы можете повторно использовать его в разных приложениях, поэтому не рекомендуется объединять его с представлением одного приложения, где вы хотите его показать, потому что обычно в разных приложениях у вас разные представления. Так что неплохо использовать публикацию / подписку для коммуникации Model-View. Когда ваша Модель изменяется, она публикует событие, View улавливает его и обновляет себя. У вас нет накладных расходов на публикацию / подписку, это помогает вам в разделении. Таким же образом вы можете сохранить логику приложения, например, в Контроллере (MVVM, MVP, это не совсем Контроллер) и сохранить представление как можно более простым. Когда ваше представление изменяется (или пользователь нажимает что-то, например), он просто публикует новое событие, контроллер улавливает его и решает, что делать. Если вы знакомы сШаблон MVC или с MVVM в технологиях Microsoft (WPF / Silverlight) вы можете думать о публикации / подписке как о шаблоне Observer . Этот подход используется в таких фреймворках, как Backbone.js, Knockout.js (MVVM).
Вот пример:
//Model
function Book(name, isbn) {
this.name = name;
this.isbn = isbn;
}
function BookCollection(books) {
this.books = books;
}
BookCollection.prototype.addBook = function (book) {
this.books.push(book);
$.publish('book-added', book);
return book;
}
BookCollection.prototype.removeBook = function (book) {
var removed;
if (typeof book === 'number') {
removed = this.books.splice(book, 1);
}
for (var i = 0; i < this.books.length; i += 1) {
if (this.books[i] === book) {
removed = this.books.splice(i, 1);
}
}
$.publish('book-removed', removed);
return removed;
}
//View
var BookListView = (function () {
function removeBook(book) {
$('#' + book.isbn).remove();
}
function addBook(book) {
$('#bookList').append('<div id="' + book.isbn + '">' + book.name + '</div>');
}
return {
init: function () {
$.subscribe('book-removed', removeBook);
$.subscribe('book-aded', addBook);
}
}
}());
Другой пример. Если вам не нравится подход MV *, вы можете использовать что-то немного другое (есть пересечение между тем, что я опишу ниже, и последним упомянутым). Просто разделите свое приложение на разные модули. Например, посмотрите Twitter.
Если вы посмотрите на интерфейс, у вас просто разные коробки. Вы можете рассматривать каждую коробку как отдельный модуль. Например, вы можете опубликовать твит. Это действие требует обновления нескольких модулей. Во-первых, он должен обновить данные вашего профиля (верхнее левое поле), но он также должен обновить вашу временную шкалу. Конечно, вы можете сохранять ссылки на оба модуля и обновлять их отдельно, используя их общедоступный интерфейс, но проще (и лучше) просто опубликовать событие. Это упростит модификацию вашего приложения из-за более слабой связи. Если вы разрабатываете новый модуль, который зависит от новых твитов, вы можете просто подписаться на событие «publish-tweet» и обработать его. Такой подход очень полезен и может сильно изолировать ваше приложение. Вы можете очень легко повторно использовать свои модули.
Вот базовый пример последнего подхода (это не оригинальный твиттер-код, это просто мой образец):
var Twitter.Timeline = (function () {
var tweets = [];
function publishTweet(tweet) {
tweets.push(tweet);
//publishing the tweet
};
return {
init: function () {
$.subscribe('tweet-posted', function (data) {
publishTweet(data);
});
}
};
}());
var Twitter.TweetPoster = (function () {
return {
init: function () {
$('#postTweet').bind('click', function () {
var tweet = $('#tweetInput').val();
$.publish('tweet-posted', tweet);
});
}
};
}());
Об этом подходе есть отличный доклад Николаса Закаса . Что касается подхода MV *, то лучшие статьи и книги, которые я знаю, опубликованы Адди Османи .
Недостатки: вы должны быть осторожны с чрезмерным использованием публикации / подписки. Если у вас есть сотни событий, управление всеми ими может стать очень запутанным. У вас также могут быть конфликты, если вы не используете пространство имен (или используете его неправильно). Расширенная реализация Mediator, которая очень похожа на публикацию / подписку, может быть найдена здесь https://github.com/ajacksified/Mediator.js . У него есть пространство имен и такие функции, как «всплытие» событий, которые, конечно, можно прервать. Еще один недостаток публикации / подписки - жесткое модульное тестирование, может стать трудным изолировать различные функции в модулях и тестировать их независимо.