Programação Funcional com Javascript — Composição
Vamos falar neste artigo 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 única, 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 entedimento.
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, 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 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 pra 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, logo nosso problema está 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 vamos pegar 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 precise da somatória do total, o que vamos fazer ?! 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, pra solucionar esse nosso pequeno problema, vamos utilizar a técnica de composição e com isso aplicaremos algo conhecido pela maioria dos Dev’s, 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á realizado 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 pra 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 existe funções que aceitem composição de forma nativa, por esse motivo, iremos implementar as funções de map e reduce de forma onde aceitem a composição.
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âmetrod, 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 post, exclusivamente, tive que estudar bastante e ler muitos outros sobre o assunto. Se você quer se aprofundar, recomendo ler os artigos e vídeo de referência.
Aqui vão as principais referências:
Top comments (1)
Gostei bastante, Iago! Foi direto e claro. Estou começando a estudar programação funcional e foi de grande ajuda.