Estratégia para substituir ninhos de if
Antes de mais nada, provavelmente existe um nome muito maneiro para este tipo de pattern, mas eu não lembro. E pra não perder a ideia do post, vou escrever sem pesquisar o nome, foi mal :cry:
Estava num grupo do telegram e vi um cara falando:
Preciso fazer este algoritmo sem usar if. Alguém poderia me ajudar?
O algoritmo era basicamente pegar o sexo, idade e tempo de trabalho para dizer se a pessoa poderia se aposentar ou não. Antes que eu pudesse responder, alguém escrever o algoritmo com um ternário, fucking gênio :sunglasses:. Mas isso é um hack.
Essa situação me fez lembrar um algoritmo de IMC que tive que fazer na faculdade, utilizando Java e ele precisava ter uma complexidade baixa e ser facilmente testável (a matéria era sobre testes). Todo mundo começou a escrever no melhor estilo GoHorse e fizeram ifs e ifs para realizar os cálculos. Eu já achei melhor parar pra pensar antes de escrever e acabei fazendo o seguinte:
Claro que em Java isso fica complexo, acabou dando uns 6 arquivos para uma coisa simples, mas atendi a todas as condições do desafio hehehe
Como o título deste post é evitar os ifs, vamos pra essa trick agora em JS/TS.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
type Sex = "M" | "F"; type Medidas = { peso: number; altura: number }; const titles = { abaixoDoPeso: "Abaixo do peso", normal: "Peso normal", poucoAcimaDoPeso: "Um pouco acima do peso", acimaDoPeso: "Acima do peso ideal", obeso: "Obesidade", }; // Os valores não condizem com o real, eu fiz TI e não medicina. // Consulte o médico e beba água const parametros = { M: [ {title: titles.abaixoDoPeso, min: 0, max: 20.7}, {title: titles.normal, min: 20.7, max: 26.4}, {title: titles.poucoAcimaDoPeso, min: 26.4, max: 27.8}, {title: titles.acimaDoPeso, min: 27.8, max: 31.1}, // Esse caso é absurdo, mas precisamos cobrir tudo acima de 31.1 {title: titles.obeso, min: 31.1, max: Number.MAX_SAFE_INTEGER}, ], F: [ {title: titles.abaixoDoPeso, min: 0, max: 19.1}, {title: titles.normal, min: 19.1, max: 25.8}, {title: titles.poucoAcimaDoPeso, min: 25.8, max: 27.3}, {title: titles.acimaDoPeso, min: 27.3, max: 32.3}, {title: titles.obeso, min: 32.3, max: Number.MAX_SAFE_INTEGER}, ], }; const calculo = ({peso, altura}: Medidas) => peso / altura ** 2; const sexo = "M" as Sex; // o input vem do usuário const altura = 1.8; const peso = 77; const imc = calculo({altura, peso}); const val = parametros[sexo].find( (medida) => medida.min <= imc && imc <= medida.max ); console.log("Status", val.title); console.log("IMC", imc);
Bom, esse é exatamente o algoritmo que fiz em Java, porém traduzido para Javascript, e em bem menos linhas. Vamos rever por partes
titles
calculo
já diz tudo, é nossa função que irá fazer o cálculo do IMCsexo
até imc
não temos nada de diferente ou gritante. Mas fica um bônus, ultimamente tenho optado por usar
parâmetros como objetos, assim posso nomear meus parâmetros recebidos na função e evita a confusão de parâmetros
posicionais, principalmente quando você não tem tiposval
fazemos um find
do objeto na nossa lista de parametros
para identificar o título, mínimo e máximo do IMCÉ isso, esse foi bem rápido, apenas para mostrar essa trick e fazer com que você evite ifs aninhados. Consegue imaginar como seria o código utilizando ifs? Bom, eu faço aqui pra você
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
if (sexo === "M") { if (0 <= imc && imc <= 20.7) { return {title: titles.abaixoDoPeso, min: 0, max: 20.7}; } if (20.7 <= imc && imc <= 26.4) { return {title: titles.normal, min: 20.7, max: 26.4}; } if (26.4 <= imc && imc <= 27.8) { return {title: titles.poucoAcimaDoPeso, min: 26.4, max: 27.8}; } if (27.8 <= imc && imc <= 31.1) { return {title: titles.acimaDoPeso, min: 27.8, max: 31.1}; } } if (sexo === "F") { // o mesmo ninho de ifs } throw Error("Algo de errado não está certo");
Viu como fica bem mais complicado? E se você precisar mudar algum valor? E se houverem novas regras? Resposta: vão haver mais e mais ifs
Mas Allan, nesse caso com vários ifs, nós temos um "else" para caso não seja atendido e no seu exemplo de objetos não tem isso
Muito perspicaz, caro leitor. Onde eu estava com a cabeça? Precisamos proteger a entrada de casos absurdos. Não é nada absurdo, apenas um if e tá "safe":
1 2 3 4 5
if (!parametros.hasOwnProperty(sexo)) { throw Error("Algo certo está errado"); } // Fluxo normal do seu programa aqui // É legal abortar os erros primeiro
Bom, agora sim ficou tudo esclarecido, assim espero. Qualquer coisa você sabe onde me encontrar :joy: