Neste artigo vamos falar sobre Composição, o que é, como resolver um determinado problema com isso e qual problema ela pode vir a resolver.
Composição
É quando podemos reunir várias funções em uma – onde a mesma recebe um número variado de funções por parâmetro, e dentro dela é usado o resultado de uma como entrada da outra. Vamos exemplificar com código, que talvez fique mais claro o entendimento.
O problema
Vamos imaginar que estamos sentados em nossa mesinha, ai o gerente chega com a gente e fala que precisa que façamos um novo módulo front-end para o sistema MarombaWebApp, e que nesse módulo, nosso amiguinho do back-end vai devolver uma lista de usuários onde nela possui alguns dados.
Imagine que sendo um deles a quantidade de parcelas faltantes e outro o valor de cada parcela, e que na nossa tela precisamos mostrar a lista de todos os usuários e o total que falta para eles pagarem. Precisamos desse controle e o prazo é pra daqui a uma hora.
Dado esse problema e o prazo apertado, precisamos entregar aquilo funcionando logo. Nós nem pensamos na qualidade, só pensamos que estamos já atrasados para entregar. Com a descrição do problema, a solução já veio na mente, uma lista é comum de receber e pensamos logo em iterar essa lista e somar o montante. Logo nosso problema estaria resolvido e ficaria dessa forma:
const usuarios = [
{ nome: 'Aluno 1', parcelasFaltantes: 3, valor: 89.9 },
{ nome: 'Aluno 2', parcelasFaltantes: 5, valor: 19.9 },
{ nome: 'Aluno 1', parcelasFaltantes: 7, valor: 49.9 },
{ nome: 'Aluno 1', parcelasFaltantes: 6, valor: 69.9 },
{ nome: 'Aluno 1', parcelasFaltantes: 2, valor: 99.9 }
]
let total = 0
usuarios.forEach(usuario => {
total += usuario.parcelasFaltantes * usuario.valor
})
console.log('valor total faltante =>', total)
Onde temos uma lista fictícia, uma variável global para receber o nosso total e um laço de repetição onde pegaremos o valor total de cada usuário.
Aparentemente não temos problema algum, não é mesmo? Mas vamos supor que precisamos dessa mesma lógica em outro módulo do sistema, dado uma lista, eu preciso da somatória do total – o que faremos? Isso mesmo que você pensou: Ctrl + C e Ctrl + V. Começando, assim, a repetição de código em todo os nosso sistema, olha que lindeza.
A solução
Como vocês já devem ter imaginado, para solucionar esse nosso pequeno problema, vamos utilizar a técnica de composição, e com isso aplicaremos algo conhecido pela maioria dos Devs – o princípio da responsabilidade única, o famoso S do SOLID.
Nosso primeiro passo vai ser separar as responsabilidades, onde teremos uma função para realizar o cálculo do valor total por usuário (linha 9) e outra função, onde será realizada a somatória total do valor faltante de todos os usuários (linha 12):
const usuarios = [
{ nome: 'Aluno 1', parcelasFaltantes: 3, valor: 89.9 },
{ nome: 'Aluno 2', parcelasFaltantes: 5, valor: 19.9 },
{ nome: 'Aluno 1', parcelasFaltantes: 7, valor: 49.9 },
{ nome: 'Aluno 1', parcelasFaltantes: 6, valor: 69.9 },
{ nome: 'Aluno 1', parcelasFaltantes: 2, valor: 99.9 }
]
const totalPorUsuario = usuario =>
usuario.parcelasFaltantes * usuario.valor;
const somatorioTotal = usuarios =>
usuarios.reduce((total, usuario) => total += totaPorUsuario(usuario), 0);
console.log('valor total faltante =>', somatorioTotal(usuarios))
Temos o mesmo resultado da nossa solução onde está tudo junto e misturado, só que dessa vez, cada coisa está no seu devido lugar, fazendo só o que precisa fazer, porém, podemos dar uma melhorada e separar a soma que é feita dentro da função somatorioTotal e criar uma outra função de soma de forma que ela se tornará genérica o suficiente para ser usada em qualquer outro lugar.
const usuarios = [
{ nome: 'Aluno 1', parcelasFaltantes: 3, valor: 89.9 },
{ nome: 'Aluno 2', parcelasFaltantes: 5, valor: 19.9 },
{ nome: 'Aluno 1', parcelasFaltantes: 7, valor: 49.9 },
{ nome: 'Aluno 1', parcelasFaltantes: 6, valor: 69.9 },
{ nome: 'Aluno 1', parcelasFaltantes: 2, valor: 99.9 }
]
const totalPorUsuario = usuario =>
usuario.parcelasFaltantes * usuario.valor;
const soma = (a, b) => a + b;
const somatorioTotal = usuarios =>
usuarios.map(totalPorUsuario).reduce(soma, 0);
console.log('valor total faltante =>', somatorioTotal(usuarios))
No JavaScript não existem funções que aceitem composição de forma nativa, por esse motivo implementaremos as funções de map e reduce onde a composição é aceita.
Elas terão a seguinte estrutura:
const map = fn => xs => xs.map(fn);
const reduce = (fn, ini) => xs => xs.reduce(fn, ini)
A função map recebe apenas uma função por parâmetro e retorna uma nova função que exige um parâmetro apenas e o parâmetro é o dado que será processado.
E temos na terceira linha a reatribuição da função reduce, onde nela temos dois parâmetros, sendo o primeiro a função que será usada para realizar o processamento dos dados e a segunda é o valor inicial. Com isso temos algo padronizado, onde a saída de uma função é a entrada da próxima.
Assim podemos organizar de forma mais eficiente o nosso código e torná-lo mais legível. As coisas vão se encaixando e fazendo sentido e fica claro o fluxo dos dados.
// Dados obtidos de algum lugar
const usuarios = [
{ nome: 'Aluno 1', parcelasFaltantes: 3, valor: 89.9 },
{ nome: 'Aluno 2', parcelasFaltantes: 5, valor: 19.9 },
{ nome: 'Aluno 1', parcelasFaltantes: 7, valor: 49.9 },
{ nome: 'Aluno 1', parcelasFaltantes: 6, valor: 69.9 },
{ nome: 'Aluno 1', parcelasFaltantes: 2, valor: 99.9 }
]
// Reatribuicao das funcoes map e reduce para suportar composicao
const map = fn => xs => xs.map(fn);
const reduce = (fn, ini) => xs => xs.reduce(fn, ini)
//Regra de negocio
const totalPorUsuario = usuario =>
usuario.parcelasFaltantes * usuario.valor;
//Separacao de responsabilidade, usando a composicao do map
const calculoTotalUsuarios = map(totalPorUsuario);
// Funcao generica
const soma = (a, b) => a + b;
// Reducao de todos os usuarios, somando todos e comecando a partir de 0
const somaTodosUsuarios = reduce(soma,0)
// Composicao das funcoes calculoTotalUsuarios e somaTodosUsuarios
const somatorioTotal = usuarios =>
somaTodosUsuarios(calculoTotalUsuarios(usuarios))
console.log('valor total faltante =>', somatorioTotal(usuarios))
Essa série de programação funcional com JavaScript foi feita de forma rápida e direta. Esse artigo, exclusivamente, tive que estudar bastante e ler muitos outros sobre o assunto. Se você quer se aprofundar, recomendo ler os artigos e o vídeo de referência.
Principais referências: