É tudo uma questão de acoplamento fraco e responsabilidade única, o que anda de mãos dadas com os padrões MV * (MVC / MVP / MVVM) em JavaScript que são muito modernos nos últimos anos.
O acoplamento fraco é um princípio orientado a objetos no qual cada componente do sistema conhece sua responsabilidade e não se preocupa com os outros componentes (ou pelo menos tenta não se importar com eles o máximo possível). O acoplamento fraco é uma coisa boa porque você pode reutilizar facilmente os diferentes módulos. Você não está acoplado às interfaces de outros módulos. Usando publicar / assinar, você só estará conectado à interface de publicar / assinar, o que não é um grande problema - apenas dois métodos. Então, se você decidir reutilizar um módulo em um projeto diferente, você pode apenas copiar e colar e provavelmente funcionará ou pelo menos você não precisará de muito esforço para fazê-lo funcionar.
Ao falar sobre acoplamento fraco, devemos mencionar a separação de interesses. Se você estiver construindo um aplicativo usando um padrão de arquitetura MV *, você sempre terá um Modelo (s) e uma Visão (ões). O modelo é a parte comercial do aplicativo. Você pode reutilizá-lo em diferentes aplicativos, portanto, não é uma boa ideia combiná-lo com a Visualização de um único aplicativo, onde deseja mostrá-lo, porque geralmente nos diferentes aplicativos você tem visualizações diferentes. Portanto, é uma boa ideia usar publicar / assinar para a comunicação Model-View. Quando seu Model muda, ele publica um evento, a View o captura e se atualiza. Você não tem nenhuma sobrecarga de publicação / assinatura, isso ajuda você no desacoplamento. Da mesma maneira, você pode manter a lógica da sua aplicação no Controlador, por exemplo (MVVM, MVP não é exatamente um Controlador) e manter a Visualização o mais simples possível. Quando sua View muda (ou o usuário clica em algo, por exemplo), ela apenas publica um novo evento, o Controller o pega e decide o que fazer. Se você estiver familiarizado com oPadrão MVC ou com MVVM em tecnologias Microsoft (WPF / Silverlight), você pode pensar em publicar / assinar como o padrão Observer . Essa abordagem é usada em estruturas como Backbone.js, Knockout.js (MVVM).
Aqui está um exemplo:
//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);
}
}
}());
Outro exemplo. Se você não gosta da abordagem MV *, pode usar algo um pouco diferente (há uma interseção entre o que vou descrever a seguir e o último mencionado). Basta estruturar sua aplicação em diferentes módulos. Por exemplo, veja o Twitter.
Se você olhar para a interface, simplesmente terá caixas diferentes. Você pode pensar em cada caixa como um módulo diferente. Por exemplo, você pode postar um tweet. Esta ação requer a atualização de alguns módulos. Em primeiro lugar, ele deve atualizar seus dados de perfil (caixa superior esquerda), mas também deve atualizar sua linha do tempo. Claro, você pode manter referências a ambos os módulos e atualizá-los separadamente usando sua interface pública, mas é mais fácil (e melhor) apenas publicar um evento. Isso tornará a modificação de seu aplicativo mais fácil devido ao acoplamento mais fraco. Se você desenvolver um novo módulo que dependa de novos tweets, você pode apenas se inscrever no evento “publicar-tweet” e lidar com isso. Essa abordagem é muito útil e pode tornar seu aplicativo muito desacoplado. Você pode reutilizar seus módulos com muita facilidade.
Aqui está um exemplo básico da última abordagem (este não é o código original do Twitter, é apenas uma amostra minha):
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);
});
}
};
}());
Para essa abordagem, há uma excelente palestra de Nicholas Zakas . Para a abordagem MV *, os melhores artigos e livros que conheço são publicados por Addy Osmani .
Desvantagens: você deve ter cuidado com o uso excessivo de publicar / assinar. Se você tem centenas de eventos, pode ficar muito confuso gerenciar todos eles. Você também pode ter colisões se não estiver usando namespacing (ou não da maneira certa). Uma implementação avançada do Mediator que se parece muito com uma publicação / assinatura pode ser encontrada aqui https://github.com/ajacksified/Mediator.js . Ele tem namespacing e recursos como “bubbling” de eventos que, é claro, podem ser interrompidos. Outra desvantagem de publicar / assinar é o teste de unidade rígido, pode ser difícil isolar as diferentes funções nos módulos e testá-los independentemente.