Javascript não é bagunça
Eu realmente gosto de Javascript. Quando vi o nascimento da plataforma node.js (node.js não é linguagem, ok?), fiquei empolgado com a idéia de poder fazer um scriptizinho.js (é ótimo ouvir isso de um gerente, não?) no lado back da força. Os primeiros exemplos de implementação sobre a plataforma não se preocupavam com a qualidade do código sugeridas pelos Jedis (Uncle Bob, Martin Fowler, Kent Beck, Booch, Gangue de Quatro* e etc.). Confesso que isso me deixava desanimado e me questionei se eu deveria investir o meu tempo neste novo mundo. Acertei! Hoje a coisa toda evoluiu e podemos afirmar que Javascript não é bagunça (ou talvez nunca tenha sido).
“TÁ” FECHADO, MAS “TÁ” ABERTO?
Quero escrever sobre um assunto que gosto muito: SOLID. Começo pela letra O do acrônimo por acreditar ser o de mais fácil implementação, inclusive em códigos existentes. Em linhas gerais o que Bertrand Meyer diz sobre o princípio do “aberto-fechado” é o seguinte:
“Uma classe só deve ser modificada quando erros forem encontrados. A classe pode ser estendida, mas nunca modificada.”
É difícil entender o valor do “aberto-fechado”. Talvez seja necessário escrever muitos códigos ruins para enxergar o real benefício deste princípio mas admito, minha vida ficou muito melhor depois que eu passei a escrever código limpo.
BUSCAFRETE — ”BUSCAPÉ” DOS FRETES
Acredito que exemplos de códigos sobre projetos factíveis são mais fáceis de entender e por isso, vamos imaginar o seguinte produto.
“Nós do grupo Eike Zords queremos construir uma solução inovadora para o setor logístico. Esse produto deverá ser capaz de retornar o menor custo de frete a partir de algumas variáveis: origem, destino e peso da mercadoria. Segundo nosso CEO, este produto já vale 1 bilhão de reais. “
Com o escopo do nosso MVP definido, chegou a hora de codar.
function CalculadoraFrete(origem,destino,peso){
var _distancia = googleMaps.getDistance(origem,destino);
var _valorFrete=[];
var _repositorio = new RepositorioTaxasFrete();
function buscaMenorFrete(){
if(peso <= 30){
if(_distancia < 50){
_valorFrete.push(_repositorio.taxaMotoFreteMunicipal());
}else{
_valorFrete.push(_repositorio.taxaCorreios());
}
}
else if(peso>30 && peso<800){
if(_distancia < 140){
_valorFrete.push(_repositorio.taxaCarro());
}else{
_valorFrete.push(_repositorio.taxaCaminhao());
}
}
else {
if(_distancia < 300){
_valorFrete.push(_repositorio.taxaCaminhao());
}else{
_valorFrete.push(_repositorio.taxaCarreta());
}
}
_valorFrete.sort();
return _valorFrete[0];
}
}
O código acima apresenta diversos problemas mas quero comentar as deficiências sobre a ótica do “open-closed principle”.
Evolução dolorosa: A classe sempre será alterada quando funcionalidades forem adicionadas no produto. É o tipo de código que em pouco tempo ninguém mais quer colocar a mão. Legibilidade: As linhas acima são difíceis de ler, será complicado criar uma linguagem ubíqua com o dono do produto. “Véio, põe gordura pra mexer nessa bagaça” será a única técnica de estimativa adequada para tarefas que envolvam mexer no código acima.
Vamos melhorar a situação desse código? Começar pelos testes é uma técnica poderosa para se encontrar bons designs de objetos.
var assert = require(“assert”); // npm install -g assert
describe(‘CalculadoraFrete’, function(){
it(‘O valor do frete tem que ser R$ 30,00 para uma distância de 15 km no mesmo município, para uma mercadoria que pesa até 10kg’, function(){
var _CEPorigem = 01314000;
var _CEPdestino = 01316000;
var _pesoDaMercadoria = 10;
var _valorDofreteEsperado = 30;
var _resultado = CalculadoraFrete.buscaMenorFrete(_CEPorigem,_CEPdestino,_pesoDaMercadoria);
assert.equal(_esperado,_resultado);
})
})
Para manter o foco sobre o assunto “open-closed” não vou copiar os códigos de testes que fiz. Eles realmente me ajudaram a evoluir o modelo.
Decidi criar uma especialização para cada tipo de frete. Deixo a responsabilidade de aceitar (ou não) fazer o cálculo do frete para algum especialista no assunto.
function TipoMotoFrete(){
this.distanciaMaximaAtendida = 50;
this.pesoMaximoAtendido = 30;
this.podeAtenderEsseFrete = function(){
return this.distancia < this.distanciaMaximaAtendida &&
this.peso < this.pesoMaximoAtendido;
}
this.obterTaxaPorKM = function() {
return 12.76;
};
this.obterTaxaPorKG = function() {
return .70;
};
}
Abaixo mais um tipo de frete
function TipoCorreioFrete(){
this.pesoMaximoAtendido = 30;
this.podeAtenderEsseFrete = function(){
return this.peso < this.pesoMaximoAtendido;
}
this.obterTaxaPorKM = function() {
return 10.97;
};
this.obterTaxaPorKG = function() {
return .65;
};
}
A distância não é importante para os “Correios” pois eles entregam em qualquer lugar do mundo! teoricamente.
Vou omitir a criação de todos os tipos de frete necessários para que otimizar o texto.
//Essa é a abstração para tipos de frete
function FornecedorFrete(distancia,peso){
if (this.constructor === FornecedorFrete) {
throw new Error(“Aqui é abstrato meu querido. Não vai estar
podendo estar sendo construído. Nós da OO center
agradecemos a compreensão.”);
};
this.podeAtenderEsseFrete = function() {
return this;
};
this.obterTaxaPorKG = function() {
return this;
};
this.obterTaxaPorKM = function() {
return this;
};
this.distancia = distancia;
this.peso = peso;
this.calculaValorFrete = function(){
var _custoPeso = this.obterTaxaPorKG() * this.peso;
var _custoDistancia = this.obterTaxaPorKM() * this.distancia;
return _custoPeso+_custoDistancia;
};
};
Chegou o momento de colocar gerência na coisa toda. Vamos implementar um cara que seja responsável por dividir o trabalho entre todos os fornecedores de frete.
function CalculadoraFrete(distancia,peso){
var _fornecedoresFrete = [];
var _fretesEncontrados = [];
_fornecedoresFrete.push(new TipoMotoFrete(distancia,peso));
_fornecedoresFrete.push(new TipoCorreioFrete(peso));
function obterMenorFrete(){
for(var i=0;i<_fornecedoresFrete.length;i++){
var _fornecedorFrete = _fornecedoresFrete[i];
if(_fornecedorFrete.podeAtenderEsseFrete()){
var _valorFrete = _fornecedorFrete.calculaValorFrete();
_fretesEncontrados.push(_valorFrete);
};
};
_fretesEncontrados.sort();
return _fretesEncontrados[0];
};
}
Terminado! Agora estamos preparados para receber diferentes tipos de fornecedores de frete e cada um deles decide se calcula o valor.
Pronto? Será que a “FornecedorFrete” não está com muita responsabilidade? Concordam que a forma de calcular o frete presente no método “calculaValorFrete” será alterada? Eu acredito que sim! Porém, quero fazer essa melhoria no momento que conversarmos sobre “Single responsibility principle” ou “cada macaco no seu galho”.