← ← ← 8/16/2022, 11:28:50 PM | Posted by: Felippe Regazio
Primeiro vamos as definições: Method Chaining (Métodos Encadeados) envolve dois importantes design patterns:
Vc pode pesquisar mais sobre esses termos se quiser se aprofundar. Você também pode procurar sobre method chain pesquisando por "The builder" design pattern.
Uma Method chain é definida por: métodos que podem ser invocados linearmente sem a necessidade de um objeto/valor intermediário. jQuery é um exemplo
$(...).height().scrollTo().remove()...
O objeto vai sendo criado (creational pattern) usando uma corrente de ações (method chain)
Em JS, para criar nossa Method Chain temos que seguir dois princípios simples:
Na prática isso fica assim:
class Person {
constructor(bodyParts) {
this.bodyParts = bodyParts;
}
addHead(n) {
this.bodyParts.head = n;
return this;
}
addLegs(n) {
this.bodyParts.legs = n;
return this;
}
addArms(n) {
this.bodyParts.arms = n;
return this;
}
}
Veja que recebemos um objeto inicializador (bodyParts) e definimos ele como uma propriedade nossa.
Depois criamos nossa chain: cada metodo manipula a propriedade bodyParts e depois retorna o contexto inteiro de volta (this). Eis a chain:
Você pode usar assim:
// simple usage
const henry = new Person().addHead(1).addArms(2).addLegs(2);
// initializing with object
const mia = new Person({ head: 1 }).addArms(2).addLegs(2);
// creatign an alien
const alien = new Person().addHead(2).addArms(4).addLegs(6);
E o resultado seria esse. Veja que cada "Pessoa" foi construída a partir da nossa chain, e o resultado foi a composição de um objeto utilizando creational pattern.
const henry = new Person().addHead(1).addArms(2).addLegs(2);
// { head: 1, arms: 2, legs: 2 }
const mia = new Person({ head: 1 }).addArms(2).addLegs(2);
// { head: 1, arms: 2, legs: 2 }
const alien = new Person().addHead(2).addArms(4).addLegs(6);
// { head: 2, arms: 4, legs: 6 }
Essa mesma técnica poderia ser usada utilizando apenas notação de função (you might not need classes in javascript) da seguinte maneira:
function Person(bodyParts = {}) {
return {
bodyParts,
addHead(n) {
this.bodyParts.head = n;
return this;
},
addLegs(n) {
this.bodyParts.legs = n;
return this;
},
addArms(n) {
this.bodyParts.arms = n;
return this;
},
}
}
Você pode usar a função como construtora:
const henry = new Person({ ... }).addLegs(n).addArms(n)...
Ou com notação de função mesmo:
const mia = Person({ ... }).addLegs(n).addArms(n)...
Ambos terão o getter:
henry.bodyParts // { arms, legs }
mia.bodyParts // { arms, legs }
De qualquer forma, se vc é mais perfecionista, deve observar que ao retornar um objeto como sendo uma chain faz com que ele venha "sujo" de metodos herdados da prototype e também tenha em seu retorno seus metodos expostos. Se você serializasse o valor gerado poderia ter problemas:
const p = new Person();
// {bodyParts: {}, addHead: ƒ, addLegs: ƒ, addArms: ƒ}
Podemos evitar essa "sujeira" fazendo com que nossa chain seja na verdade o prototype do nosso objeto manipulado:
function Person(bodyParts = {}) {
const chain = Object.create({
addHead(n) {
this.bodyParts.head = n;
return this;
},
addLegs(n) {
this.bodyParts.legs = n;
return this;
},
addArms(n) {
this.bodyParts.arms = n;
return this;
},
});
chain.bodyParts = bodyParts;
return chain;
}
Da forma acima assinamos a nossa chain como um objeto tendo nossos metodos como prototype. E após isso assimos ao objeto gerado a propriedade bodyParts. Agora:
const p = new Person();
// {bodyParts: {}}
Parabéns, você acaba de aprender o "Method Chaining Design Pattern". Eu usei exemplos bem dummy pra tentar simplificar a explicação, mas com um pouquinho de tecnica e criatividade vc consegue fazer variações e obter resultados muito legais com ele.