JavaScript Sem if: Criando Código Inteligente com Mapas de Decisão



Objetivo

O objetivo deste artigo é apresentar uma abordagem mais moderna, escalável e inteligente para lidar com estruturas condicionais em JavaScript, substituindo o uso excessivo de if/else e switch por uma técnica moderna baseada em Mapas de Decisão (Decision Maps) que melhora a legibilidade, escalabilidade e manutenção do código.

Veja um exemplo comum:

function calcularDesconto(tipo) {
    if (tipo === "cliente") return 0.1;
    else if (tipo === "funcionario") return 0.2;
    else if (tipo === "vip") return 0.3;
    else return 0;
}

Agora imagine isso com 10, 20 ou 50 regras, vira um pesadelo para a manutenção do código.

A boa notícia é que isso pode ser resolvido pela técnica de Mapas de Decisão (Decision Maps), que apresentaremos neste artigo.

Ao final da leitura, você será capaz de:

  • Reduzir a complexidade de estruturas condicionais
  • Tornar seu código mais legível e organizado
  • Criar sistemas mais fáceis de manter e expandir
  • Aplicar uma técnica amplamente utilizada em arquiteturas modernas

Este conteúdo é especialmente útil para desenvolvedores que já dominam o básico de JavaScript e desejam evoluir para um nível mais profissional, adotando padrões que melhoram a qualidade do código.

Referências

Criando objetos e o uso do "this" no JavaScript

O que são Arrow Functions e como usar no JavaScript

Como criar funções com JavaScript

Definições

Antes de avançarmos para a prática, é importante alinhar alguns conceitos essenciais.

🔹 O que são Estruturas Condicionais

Estruturas condicionais são blocos de código que executam diferentes ações com base em determinadas condições.

Os exemplos mais comuns em JavaScript são:

  • if / else
  • switch

Essas estruturas fazem parte do paradigma imperativo, onde o fluxo do programa é controlado explicitamente por decisões lógicas.

🔹 Paradigma: De Lógica para Dados

Essa técnica representa uma mudança importante de mentalidade:

Abordagem Característica
Imperativa Baseada em if e fluxo de controle
Orientada a dados Baseada em estruturas e mapeamentos

No modelo orientado a dados:

  • as regras ficam centralizadas
  • o código se torna mais previsível
  • alterações exigem menos esforço

🔹 O que é um Mapa de Decisão (Decision Map)

Um Mapa de Decisão é uma estrutura de dados (abordagem orientada a dados) — geralmente um objeto ou um Map — utilizada para associar chaves a comportamentos ou valores.

Em vez de perguntar:

"Se condição A, faça isso; se B, faça aquilo..."

Você passa a definir:

"Para cada chave, existe uma ação correspondente"

Ou seja: 👉 você troca lógica condicional por uma tabela de decisões.

Exemplo onde usamos um objeto como tabela de decisão, em vez de usar if.

const descontos = {
    cliente: 0.1,
    funcionario: 0.2,
    vip: 0.3
};

function calcularDesconto(tipo) {
    return descontos[tipo] || 0;
}

Você transformou: ➡ lógica condicional (abordagem imperativa) em ➡ estrutura de dados (abordagem orientada a dados)

🔹 O que é Dispatch Dinâmico

Outro conceito importante aqui é o dispatch dinâmico.

Isso significa que o comportamento do código é definido em tempo de execução, com base em uma chave ou valor.

Exemplo conceitual:

acoes[acao]() 

Aqui:

  • acao define o comportamento
  • o sistema escolhe automaticamente qual função executar

Sem precisar de if.

🔹Diferença de Mapa de Decisão e Dispatch Dinâmico

A tabela abaixo resume a diferença fundamental entre usar objetos para consultar dados (Mapa de Decisão) ou para disparar ações (Dispatch Dinâmico) de forma limpa e eficiente.

🔄 Exemplos práticos com Mapas de Decisão

Agora que você já entende o conceito, vamos aplicar na prática.

A proposta é simples:

Para cada estrutura com if/else, existe uma forma equivalente usando mapas de decisão.

🔹 Caso 1 — if simples

✔ Código tradicional

<p id="resultado1"></p>

<script>
function obterStatus(ativo) {
    if (ativo) {
        return "Ativo";
    }
}

document.getElementById("resultado1").textContent = obterStatus(true);
</script>

🚀 Usando mapa de decisão

<p id="resultado2"></p>

<script>
const statusMap = {
    true: "Ativo"
};

function obterStatus(ativo) {
    return statusMap[ativo];
}

document.getElementById("resultado2").textContent = obterStatus(true);
</script>

Resultado (usamos aqui o Squids Editor)

Explicação

Em vez de usar um if (ativo === true) { return "Ativo" }, você criou um objeto chamado statusMap.

  • O Mapa (statusMap): Funciona como um dicionário. Ele guarda que a chave true corresponde ao texto "Ativo".
  • A Função (obterStatus): Ela não pergunta "o que é isso?". Ela simplesmente acessa a "gaveta" do objeto usando o valor recebido.
  • A Exibição: O document.getElementById localiza o parágrafo e injeta o texto encontrado.

🔹 Caso 2 — if / else

✔ Código tradicional

<p id="resultado3"></p>

<script>
function obterStatus(ativo) {
    if (ativo) {
        return "Ativo";
    } else {
        return "Inativo";
    }
}

document.getElementById("resultado3").textContent = obterStatus(false);
</script>

🚀 Usando mapa de decisão

<p id="resultado4"></p>

<script>
const statusMap = {
    true: "Ativo",
    false: "Inativo"
};

function obterStatus(ativo) {
    return statusMap[ativo];
}

document.getElementById("resultado4").textContent = obterStatus(false);
</script>

Resultado (usamos aqui o Squids Editor)

Explicação

Em vez de usar uma estrutura if/else, você criou uma Tabela de Consulta (Lookup Table).

  • O Mapa (statusMap): Funciona como um dicionário centralizado que associa chaves (true, false) a valores amigáveis ("Ativo", "Inativo").
  • O Acesso (statusMap[ativo]): A função não testa condições. Ela usa o valor da variável ativo como uma "chave" para acessar diretamente a propriedade do objeto.
  • A Exibição: O document.getElementById busca o elemento no HTML e injeta o texto retornado pela função.

🔹 Caso 3 — if / else if / else (faixas de valor)

✔ Código tradicional

<p id="resultado5"></p>

<script>
function classificarNota(nota) {
    if (nota >= 90) return "A";
    else if (nota >= 70) return "B";
    else if (nota >= 50) return "C";
    else return "D";
}

document.getElementById("resultado5").textContent = classificarNota(75);
</script>

🚀 Usando mapa de decisão com estrutura de regras

<p id="resultado6"></p>

<script>
const classificacoes = [
    { limite: 90, resultado: "A" },
    { limite: 70, resultado: "B" },
    { limite: 50, resultado: "C" },
    { limite: 0,  resultado: "D" }
];

function classificarNota(nota) {
    return classificacoes.find(regra => nota >= regra.limite).resultado;
}

document.getElementById("resultado6").textContent = classificarNota(75);
</script>

Resultado (usamos aqui o Squids Editor)

Explicação

Em vez de um emaranhado de if/else if para checar faixas de notas, você criou uma lista de critérios.
  • A Estrutura de Regras (classificacoes): É um array de objetos que define os limites. Note que eles estão em ordem decrescente (do maior para o menor).
  • A Busca (find): A função percorre essa lista e para na primeira regra que for verdadeira para a nota fornecida (ex: 75 é maior ou igual a 70? Sim).
  • A Exibição: O resultado ("B") é extraído do objeto encontrado e injetado no HTML.

Na Estrutura de Regras: O código precisa processar e comparar os dados. Ele percorre a lista testando uma lógica (nota >= regra.limite) até encontrar uma correspondência.

🔹 Caso 4 — Substituindo switch

✔ Código tradicional

<p id="resultado7"></p>

<script>
function obterMensagem(tipo) {
    switch (tipo) {
        case "sucesso":
            return "Operação concluída";
        case "erro":
            return "Ocorreu um erro";
        case "alerta":
            return "Atenção!";
        default:
            return "Desconhecido";
    }
}

document.getElementById("resultado7").textContent = obterMensagem("erro");
</script>

🚀 Usando mapa de decisão com fallback

<p id="resultado8"></p>

<script>
const mensagens = {
    sucesso: "Operação concluída",
    erro: "Ocorreu um erro",
    alerta: "Atenção!"
};

function obterMensagem(tipo) {
    return mensagens[tipo] || "Desconhecido";
}

document.getElementById("resultado8").textContent = obterMensagem("erro");
</script>

Resultado (usamos aqui o Squids Editor)

⚠️ OBSERVAÇÃO IMPORTANTE: Para que o código fique mais seguro e não falhar com valores válidos tipo 0 ou "" (string vazia), substitua || (OU lógico) por ?? (Coalescência Nula), conforme o padrão moderno (ES2020+).

  • || (OR lógico): Retorna o primeiro valor verdadeiro (truthy).
  • ?? (Coalescência Nula): Retorna o primeiro valor definido (não null nem undefined).
return mensagens[tipo] ?? "Desconhecido";

Explicação

Você criou um sistema de mensagens que funciona como um catálogo, mas com uma "rede de proteção".
  • O Mapa (mensagens): Um objeto que centraliza os textos do sistema.
  • O Acesso Seguro (fallback): A função tenta buscar a chave (ex: "erro") por intermédio da variável tipo. Se ela não encontrar nada no mapa (ou seja, se o resultado for undefined), o operador || (OU) entra em ação e entrega o texto "Desconhecido".
  • A Exibição: O document.getElementById injeta o resultado final ("Ocorreu um erro") no parágrafo do HTML.

Acesso Seguro (fallback): A função tenta buscar a chave (ex: "erro") no mapa por meio da variável tipo. Se a chave não existir, o resultado será undefined. Para evitar isso, utilizamos um valor padrão (fallback).

Existem duas formas comuns de fazer isso:

  • Operador lógico OU (||)
    Retorna o valor à direita caso o da esquerda seja falso (false, 0, "", null, undefined, etc.).
  • Operador de Coalescência Nula (??)
    Retorna o valor à direita apenas se o da esquerda for null ou undefined.

💡 Boa prática: prefira ??, pois ele evita substituir valores válidos como 0, false ou strings vazias.

🔹 Caso 5 — Executando ações - Dispatch Dinâmico

✔ Código tradicional

<p id="resultado9"></p>

<script>
function executar(tipo) {
    if (tipo === "ola") {
        return "Olá, usuário!";
    } else if (tipo === "bye") {
        return "Até logo!";
    }
}

document.getElementById("resultado9").textContent = executar("ola");
</script>

🚀 Usando mapa de funções

<p id="resultado10"></p>

<script>
const acoes = {
    ola: () => "Olá, usuário!",
    bye: () => "Até logo!"
};

function executar(tipo) {
    return acoes[tipo]?.() || "Ação inválida";
}

document.getElementById("resultado10").textContent = executar("ola");
</script>

Resultado (usamos aqui o Squids Editor)

⚠️ OBSERVAÇÃO IMPORTANTE: Para que o código fique mais seguro e não falhar com valores válidos tipo 0 ou "" (string vazia), substitua || (OU lógico) por ?? (Coalescência Nula), conforme o padrão moderno (ES2020+).

  • || (OR lógico): Retorna o primeiro valor verdadeiro (truthy).
  • ?? (Coalescência Nula): Retorna o primeiro valor definido (não null nem undefined).
return acoes[tipo]?.() ?? "Ação inválida";

Explicação

Você criou um "menu de comandos" onde cada chave dispara uma ação específica.
  • O Mapa de Funções (acoes): É um objeto onde os valores são funções de seta (arrow functions). Isso permite que cada opção execute uma lógica própria.
  • O Acesso com Execução (acoes[tipo]?.()): A função executar busca a ação e, se ela existir, a executa imediatamente usando os parênteses ().
  • A Exibição: O resultado retornado pela função escolhida ("Olá, usuário!") é injetado no HTML.
Aqui, o Dispatch Dinâmico é usado para decidir qual bloco de código deve rodar em tempo de execução.
  • O Operador ?. (Optional Chaining): Esta é uma técnica de mestre. Ele verifica: "A função existe?". Se sim, ele a chama. Se não (caso você peça uma ação que não está no mapa), ele retorna undefined sem quebrar o programa.
  • O Fallback (|| "Ação inválida"): Se o dispatch falhar (porque a ação não existe), o código entrega uma mensagem de erro amigável em vez de travar.

🔹 Caso 6 — União de Mapa de Decisão e Dispatch Dinâmico

Aqui está um exemplo prático de um Sistema de Checkout. Ele é perfeito porque em uma única ação (finalizar a compra), nós precisamos consultar dados (Mapa de Decisão) e executar uma ação (Dispatch Dinâmico).

<div id="status-compra">Aguardando finalização...</div>

<script>
// MAPA DE DECISÃO: Retorna apenas dados (o produto)
const CATALOGO = {
    'cod_1': { nome: "Notebook Pro", preco: 5000 },
    'cod_2': { nome: "Mouse Gamer", preco: 200 }
};

// DISPATCH DINÂMICO: Executa comportamentos (o pagamento)
const PROCESSAR_PAGAMENTO = {
    PIX: (valor) => `Pagamento de R$ ${valor} via PIX confirmado! ✨`,
    CARTAO: (valor) => `R$ ${valor} parcelado no cartão com sucesso! 💳`,
    BOLETO: (valor) => `Boleto de R$ ${valor} gerado. Aguardando compensação. 📄`
};

function finalizarCompra(idProduto, metodo) {
    const display = document.getElementById("status-compra");

    // 1. Usando Mapa de Decisão para buscar o produto (Retorna Valor)
    const produto = CATALOGO[idProduto] ?? { nome: "Desconhecido", preco: 0 };

    // 2. Usando Dispatch Dinâmico para processar o pagamento (Executa Ação)
    const acaoPagamento = PROCESSAR_PAGAMENTO[metodo];

    // Executamos a lógica e montamos a mensagem final
    const mensagemFinal = acaoPagamento 
        ? acaoPagamento(produto.preco) 
        : "Método de pagamento inválido.";

    display.textContent = `Produto: ${produto.nome} | ${mensagemFinal}`;
}

// Simulando a finalização de uma compra
finalizarCompra('cod_1', 'PIX');
</script>

Resultado (usamos aqui o Squids Editor)

Explicação

  1. Mapa de Decisão (CATALOGO): Fomos direto na "gaveta" cod_1 e pegamos as informações do Notebook. É uma consulta estática de valores.
  2. Dispatch Dinâmico (PROCESSAR_PAGAMENTO): Não apenas pegamos uma string, mas disparamos a função correspondente ao PIX, passando o preço do produto como parâmetro para ela processar.
Por que isso é código de padrão utilizado em sistemas reais?
  • Sem if ou switch: O fluxo é totalmente guiado pelos dados.
  • Extensível: Quer adicionar o produto "Teclado"? Só mexe no CATALOGO. Quer aceitar "Cripto"? Só adiciona uma função no PROCESSAR_PAGAMENTO.
  • Responsabilidade Única: Cada objeto cuida da sua parte (um cuida dos produtos, outro dos pagamentos).

✅ Quando os Mapas de Decisão são Ideais

Os Mapas de Decisão brilham quando você deseja escrever código declarativo, ou seja, focado em o que deve ser feito, e não como fazer passo a passo.

Eles são especialmente eficazes nas seguintes situações:

🔹 Múltiplas opções baseadas em uma chave (Mapeamento Direto)

Sempre que você possui um valor de entrada único (como "PENDENTE", "PAGO", "CANCELADO") e precisa associá-lo diretamente a um resultado ou comportamento.

👉 É o substituto natural para estruturas switch extensas.

🔹 Regras bem definidas e estáticas

Quando a relação entre entrada e saída é fixa e previsível.

👉 Aqui o mapa funciona como uma tabela de decisão explícita.

🔹 Sistemas que exigem escalabilidade (Open/Closed Principle)

Um dos maiores ganhos dessa abordagem é aderir ao princípio:

Aberto para extensão, fechado para modificação

Você pode adicionar novos comportamentos sem alterar a lógica existente.

👉 Isso reduz riscos e facilita manutenção em sistemas grandes.

🔹 Centralização de regras e configuração

Mapas permitem separar claramente:

  • Configuração (dados)
  • Execução (lógica)

Isso traz vantagens importantes:

✔ Código mais organizado
✔ Facilidade para testes unitários
✔ Melhor legibilidade
✔ Possibilidade de carregar regras dinamicamente (ex: JSON, APIs)

🔹 Dispatch dinâmico (comportamento sob demanda)

Quando cada chave representa uma ação (função), você ganha um nível extra de flexibilidade.

👉 O sistema decide em tempo de execução qual comportamento executar.

❌ Quando NÃO usar (prefira if/else)

Nem toda lógica deve ser convertida em mapa. Forçar essa abordagem pode resultar em código artificial, difícil de entender e manter.

🔹 Múltiplas condições interdependentes (Lógica Booleana)

Quando a decisão depende de várias variáveis combinadas:

if (idade > 18 && temConvite && !estaBanido)

👉 Mapas não lidam bem com combinações lógicas complexas.

✔ Aqui, o if é mais claro, direto e semântico.

🔹 Regras baseadas em intervalos ou comparações

Mapas funcionam melhor com igualdade direta (chaves).

Quando há comparações como:

  • maior que (>)
  • menor que (<)
  • intervalos

👉 A solução com mapas se torna artificial.

✔ Melhor usar:

  • if/else
  • ou estruturas híbridas (como arrays de regras)

🔹 Fluxos com múltiplos passos e efeitos colaterais

Se cada decisão envolve:

  • cálculos intermediários
  • alterações de estado
  • várias etapas encadeadas

👉 Esconder isso dentro de um mapa pode prejudicar a legibilidade.

✔ Nesses casos, o fluxo explícito do if é mais transparente.

🔹 Lógica de saída antecipada (Early Return)

Quando você precisa interromper o fluxo rapidamente:

if (!usuario) return;

👉 O if comunica claramente a intenção de parada.

✔ Mapas não são adequados para esse tipo de controle de fluxo.

🔹 Quando a legibilidade é prioridade absoluta

Código “esperto demais” pode ser pior que código simples.

👉 Se o mapa tornar a leitura menos óbvia para outros desenvolvedores:

✔ prefira if/else

🧠 Regra prática 

Você pode resumir assim no artigo:

Use Mapas de Decisão quando a lógica for baseada em escolha por chave.
Use if/else quando a lógica depender de condições, relações ou fluxo.

Onde esse padrão é usado

Mapas de decisão e dispatch dinâmico são amplamente utilizados em:

  • frameworks JavaScript
  • roteadores de APIs
  • sistemas embarcados (como Arduino)
  • engines de regras
  • automação de processos

Esse padrão é comum em código interno de frameworks e bibliotecas modernas, sendo amplamente adotado em sistemas que precisam de flexibilidade e escalabilidade.

Conclusão

Parar de depender de if não é apenas uma questão de estilo.

É uma evolução na forma de pensar o código.

Você deixa de escrever lógica dispersa e passa a trabalhar com estruturas organizadas, previsíveis e escaláveis.

O anúncio abaixo ajuda manter o Portal Visual Dicas

Comentários

×

Infomações do site / SEO