Em meu último artigo, discuti o Padrão de projeto de software Singleton e como ele é usado em JavaScript. Desta vez, o padrão de projeto Bridge ficou martelando em sua cabeça e pedindo um pouco de atenção, tornando-o o segundo da série Padrão de projeto de software – JavaScript.
Todos os lugares em que li sobre o padrão Bridge gostam de citar o “Gang of Four” diretamente em sua descrição do que é o padrão Bridge, então pensei: por que não eu também? O padrão Bridge deve “desacoplar uma abstração de sua implementação, de modo que as duas possam variar independentemente.” Bridges são muito benéficas em caso de aplicações orientadas a eventos, que são bastante comuns em JavaScript. Considerando esse fato, é surpreendentemente um dos padrões de design mais subutilizados.
Exemplo de evento Listeners
Nos exemplos a seguir, vou usar um pouco de jQuery, então se você não sabe o que uma função faz ou como ela funciona, pode acessar a documentação.
Abaixo você verá um pequeno pedaço de código usando um método de API chamado getXById, que é mal implementado. Estamos usando um evento de clique para determinar de qual elemento puxar o ID . O retorno em si é getXById, que recebe o id do elemento clicado e usa AJAX para obter X do servidor usando o Id encontrado.
getXById = function() {
var id = this.id;
$.ajax({
url:'/getx?id=' + id,
success: function(response) {
console.log(response);
}
});
}
$('someElement').bind('click', getXById);
Esse código não é de todo ruim, se é só para ser usado em uma forma específica na página específica de alguém, mas é (supostamente) parte de uma API, então isso precisa ser muito mudado. Vamos desacoplar getXById do evento Listener e da implementação do que é feito com o resultado:
getXById = function(id, callback) {
$.ajax({
url:'/getx?id=' + id,
success: callback
}
}
getXBridge = function() {
var id = this.id;
getXById(this.id, function() {
console.log(response);
});
}
$('someElement').bind('click', getXBridge);
Agora, o getXById pode ser usado em qualquer lugar e você pode fazer qualquer coisa com X.
Exemplo clássico
O que quero dizer com “clássico” é duplo: o exemplo é mais comum para linguagens clássicas orientadas a objetos de programação e suas classes de usos.
Tecnicamente, o JavaScript não tem classes, mas você simula interfaces e usa protótipos para fazer o mesmo com as classes. Originalmente, este exemplo estava no livro Head First Design Patterns, que usa Java em seu exemplo. No entanto, esse é um dos menores padrões na parte de trás do livro e que não tinha realmente um exemplo de código, então estou usando apenas os diagramas (exceto os que recriei, porque sou incrível).
Nosso produto de partida
Começamos com a interface RemoteControl. ToshibaRemote e SonyRemote implementam essa interface para trabalhar com seus respectivos televisores. Com esse código, você pode chamar on(), off(), ou setChannel() em qualquer controle remoto e, mesmo que todas as TVs forem diferentes, irá funcionar. O que acontece, porém, quando você quer fazer melhorias nos controles remotos? É aí que vem o padrão Bridge:
Agora, uma vez que as televisões aderiram a uma interface e todos os controles remotos aderiram a outra – na verdade, apenas uma classe, pois só precisa de uma implementação – podemos criar variações por meio de herança e ainda ser compatível. Quer ver algum código? Vou te mostrar o código para a nova solução com o padrão Bridge, mas não acho que você precise ver o código para o original. Realmente não acho que muitos de vocês precisam ver qualquer código, mas tenho certeza de que há pessoas que gostariam de vê-lo de qualquer maneira. Nós somos programadores, certo? Mostre-nos o código!
var RemoteControl = function(tv) {
this.tv = tv;
this.on = function() {
this.tv.on();
};
this.off = function() {
this.tv.off();
};
this.setChannel = function(ch) {
this.tv.tuneChannel(ch);
};
};
/* Newer, Better Remote Control */
var PowerRemote = function(tv) {
this.tv = tv;
this.currChannel = 0;
this.setChannel = function(ch) {
this.currChannel = ch;
this.tv.tuneChannel(ch);
};
this.nextChannel = function() {
this.setChannel(this.currChannel + 1);
};
this.prevChannel = function() {
this.setChannel(this.currChannel – 1);
};
};
PowerRemote.prototype = new RemoteControl();
/** TV Interface
Since there are no Interfaces in JavaScript I'm just
going to use comments to define what the implementors
should implement
function on
function off
function tuneChannel(channel)
*/
/* Sony TV */
var SonyTV = function() {
this.on = function() {
console.log('Sony TV is on');
};
this.off = function() {
console.log('Sony TV is off');
};
this.tuneChannel = function(ch) {
console.log('Sony TV tuned to channel ' + ch);
};
}
/* Toshiba TV */
var ToshibaTV = function() {
this.on = function() {
console.log('Welcome to Toshiba entertainment');
};
this.off = function() {
console.log('Goodbye Toshiba user');
};
this.tuneChannel = function(ch) {
console.log('Channel ' + ch + ' is set on your Toshiba television');
};
}
/* Let's see it in action */
var sony = new SonyTV(),
toshiba = new ToshibaTV(),
std_remote = new RemoteControl(sony),
pwr_remote = new PowerRemote(toshiba);
std_remote.on(); // prints "Sony TV is on"
std_remote.setChannel(55); // prints "Sony TV tuned to channel 55"
std_remote.setChannel(20); // prints "Sony TV tuned to channel 20"
std_remote.off(); // prints "Sony TV is off"
pwr_remote.on(); // prints "Welcome to Toshiba entertainment"
pwr_remote.setChannel(55); // prints "Channel 55 is set on your Toshiba television"
pwr_remote.nextChannel(); // prints "Channel 56 is set on your Toshiba television"
pwr_remote.prevChannel(); // prints "Channel 55 is set on your Toshiba television"
pwr_remote.off(); // prints "Goodbye Toshiba user"
Bem, são somente dicas para o padrão Bridge em JavaScript. Se você não tem ainda, certifique-se de voltar e ler sobre o padrão Singleton. Também fique ligado para o próximo artigo desta série: o padrão Composite.
?
Texto original disponível em http://www.joezimjs.com/javascript/javascript-design-patterns-bridge/