Este é o capítulo final da série padrão de projeto de software – JavaScript. Hoje, nós vamos falar sobre o padrão Chain of Responsibility. Ele separa o sender e o receiver de um request. Isso é feito com uma cadeia de objetos, cada um dos quais pode lidar com o request em si ou passá-lo para o próximo objeto. Confuso? Leia mais.
Estrutura do Chain of Responsibility
Existem três partes do padrão Chain of Responsibility: sender, receiver e request. O sender faz o request. O receiver é uma cadeia de um ou mais objetos que escolhe se quer lidar com o request ou transmiti-lo. O request em si pode ser um objeto que encapsula todos os dados apropriados.
Um sender envia o request para o objeto receiver que está em primeiro lugar na cadeia. O sender só sabe sobre esse primeiro objeto, e nada sobre os outros receivers. O primeiro receiver ou lida com o request e/ou passa para o próximo na cadeia. Cada receiver só conhece o receiver seguinte na linha. O request vai continuar abaixo da linha até que ele seja tratado ou não existirem mais receivers para passá-lo – nesse ponto, nada acontece ou é lançado um erro, dependendo de como você quer que ele funcione.
Cadeias no mundo que nos rodeia
A manipulação de eventos no DOM usa uma implementação do Chain of Responsibility (incrível como muitos padrões são utilizados em conjunto no DOM). Uma vez que um evento é acionado, ele chama a hierarquia DOM, executando cada manipulador de eventos, até que chegue ao final da cadeia ou um manipulador dizer a ele para parar a propagação.
Exemplo de Chain of Responsibility
Para o nosso exemplo de hoje, vamos criar um caixa eletrônico. A cadeia vai consistir de contas de diferentes tamanhos. Quando você pede dinheiro, a máquina começa nas contas maiores e tira quantas é necessário e, então, vai para as contas menores. Este exemplo é muito simples, o que ajuda a mostrar o conceito mais claramente sem diluir o código com implementações que são específicas para o exemplo.
Vamos começar criando a classe receptora: MoneyStacks. Normalmente, isso seria apenas uma classe abstrata ou interface que seria subclassificada/implementada para criar vários receptores diferentes, mas este exemplo é tão simples que a única variação entre cada um dos receptores será o tamanho das contas na pilha, de modo que podemos apenas definir esse número por meio de um parâmetro no construtor.
var MoneyStack = function(billSize) {
this.billSize = billSize;
this.next = null;
}
MoneyStack.prototype = {
withdraw: function(amount) {
var numOfBills = Math.floor(amount / this.billSize);
if (numOfBills > 0) {
// Eject the bills
this._ejectMoney(numOfBill);
// Shrink the amount by how much money we ejected
amount = amount – (this.billSize * numOfBills);
}
// If there is any money left to withdraw and if we have
// another stack in the line, pass the request on
amount > 0 && this.next && this.next.withdraw(amount);
},
// set the stack that comes next in the chain
setNextStack: function(stack) {
this.next = stack;
},
// private method that ejects the money
_ejectMoney: function(numOfBills) {
console.log(numOfBills + " $" + this.billSize
+ " bill(s) has/have been spit out");
}
}
É tudo uma matemática muito simples. withdraw é a função que usa a capacidade de encadeamento por expulsar as contas necessárias e passar o pedido quando apropriado.
Agora, vamos construir o caixa eletrônico. Seu construtor cria todas as pilhas de dinheiro e as coloca em sua ordem hierárquica. Quando alguém chama o método withdraw, ele só passa a responsabilidade para a cadeia de pilhas de dinheiro.
var ATM = function() {
// Create the stacks of money
// We'll show you the implementation for this next
var stack100 = new MoneyStack(100),
stack50 = new MoneyStack(50),
stack20 = new MoneyStack(20),
stack10 = new MoneyStack(10),
stack5 = new MoneyStack(5),
stack1 = new MoneyStack(1);
// Set the hierarchy for the stacks
stack100.setNextStack(stack50);
stack50.setNextStack(stack20);
stack20.setNextStack(stack10);
stack10.setNextStack(stack5);
stack5.setNextStack(stack1);
// Set the top stack as a property
this.moneyStacks = stack100;
}
ATM.prototype.withdraw = function(amount) {
this.moneyStacks.withdraw(amount);
}
// USAGE
var atm = new ATM();
atm.withdraw(186);
/* outputs:
1 $100 bill(s) has/have been spit out
1 $50 bill(s) has/have been spit out
1 $20 bill(s) has/have been spit out
1 $10 bill(s) has/have been spit out
1 $5 bill(s) has/have been spit out
1 $1 bill(s) has/have been spit out
*/
atm.withdraw(72);
/* outputs:
1 $50 bill(s) has/have been spit out
1 $20 bill(s) has/have been spit out
2 $1 bill(s) has/have been spit out
*/
Terminando minhas responsabilidades
Isso é tudo que existe para este padrão. É muito simples. Como os padrões Command e Observer, seu objetivo é separar os senders e os receivers, mas por razões diferentes e com diferentes trade-offs. Devido à sua estrutura de hierarquia, também é semelhante ao padrão Composite, e pode ser injetado dentro dele para tornar alguns dos métodos mais eficientes.
Bem, tem sido divertido levá-lo por todos esses padrões de projeto de JavaScript. Espero que você tenha aprendido alguma coisa ao longo do caminho. Se você não tiver lido todos, eu recomendo que você o faça. A lista está abaixo. Lembre-se, porém, de que só porque você conhece um padrão, não significa que é necessário para a tarefa que você tem que fazer no momento (você conhece o velho ditado, “quando tudo que você tem é um martelo, tudo parece um prego”).
Como sempre: Happy Coding!!
Série padrão de projeto de software – JavaScript:
- Padrão Singleton
- Padrão Bridge
- Padrão Composite
- Padrão Facade
- Padrão Adapter
- Padrão Decorator
- Padrão Factory Parte 1
- Padrão Factory Parte 2
- Padrão Proxy
- Padrão Observer
- Padrão Command
?
Texto original disponível em http://www.joezimjs.com/javascript/javascript-design-patterns-chain-of-responsibility/