7 minutos
Por que não uso else nos meus códigos

Como desenvolvedores, estamos constantemente buscando maneiras de melhorar a qualidade e a manutenibilidade do nosso código. Uma prática que adotei é evitar o uso de declarações else no meu código. Essa abordagem, inspirada no Object Calisthenics e considerações sobre Complexidade Ciclomática, levou a um código mais limpo, legível e fácil de manter.

Object Calisthenics e Complexidade Ciclomática

Object Calisthenics é um conjunto de exercícios de programação projetados para melhorar a forma como escrevemos código orientado a objetos. Uma de suas regras sugere limitar cada método a apenas um nível de indentação. Essa regra naturalmente leva a evitar declarações else, pois elas frequentemente introduzem níveis adicionais de indentação.

A Complexidade Ciclomática, por outro lado, é uma métrica que mede o número de caminhos linearmente independentes através do código-fonte de um programa. Uma complexidade ciclomática menor geralmente indica um código mais simples e mais fácil de manter. Ao evitar declarações else, podemos frequentemente reduzir a complexidade ciclomática.

Assim, utilizando o else temos:

Indentação excessiva — o uso excessivo da estrutura de controle if aninhada significa que há um alto nível de indentação que dificulta a leitura do código.

Relação entre if-else — quando há uma grande quantidade de trechos de código separados entre if-else, que estão conceitualmente relacionados entre si, é necessário ler o código saltando entre as diferentes partes.

Esforço mental — uma consequência dos diferentes saltos no código fonte é que isso gera um esforço extra na criação do código.

O Padrão Early Return

A chave para evitar declarações else é o padrão Early Return (Retorno Antecipado). Esse padrão envolve retornar de uma função assim que uma condição é atendida, em vez de aninhar condições umas dentro das outras. Vamos ver um exemplo:

// Sem early return
function processarUsuario(usuario: Usuario): string {
  if (usuario.estaAtivo) {
    if (usuario.temPermissao("admin")) {
      return "Bem-vindo, administrador!";
    } else {
      return "Bem-vindo, usuário!";
    }
  } else {
    return "Conta inativa";
  }
}

Neste código, há dois níveis de aninhamento, o que pode complicar a leitura e manutenção. Usando o padrão Early Return, podemos refatorar o código para simplificar a estrutura:

// Com early return
function processarUsuario(usuario: Usuario): string {
  if (!usuario.estaAtivo) {
    return "Conta inativa";
  }

  if (usuario.temPermissao("admin")) {
    return "Bem-vindo, administrador!";
  }

  return "Bem-vindo, usuário!";
}

Vamos ao breakdown:

  1. Inverter a Condição Negativa: O primeiro if foi alterado para verificar a condição !usuario.estaAtivo. Isso permite que a função retorne imediatamente se o usuário não estiver ativo, evitando a necessidade de um bloco else.

  2. Eliminar o Primeiro else: Ao retornar imediatamente quando usuario.estaAtivo é false, eliminamos a necessidade de um bloco else que estaria presente se a condição if fosse falsa.

  3. Reduzir o Aninhamento: O código agora tem uma estrutura plana, sem níveis adicionais de indentação. A segunda condição if é verificada apenas quando o usuário está ativo. Se o usuário tiver permissão de “admin”, retornamos a mensagem correspondente. Caso contrário, retornamos a mensagem padrão para usuários normais.

  4. Finalizar com um Retorno Padrão: Se nenhuma das condições anteriores for atendida (ou seja, se o usuário estiver ativo mas não for um “admin”), retornamos a mensagem para o usuário padrão.

Viu que não é tão difícil?

Exemplo do Mundo Real

Vamos olhar para um exemplo mais complexo onde evitar else pode melhorar significativamente a qualidade do código:

interface Pedido {
  itens: number;
  total: number;
  tipoUsuario: "regular" | "premium" | "vip";
}

// Sem early return
function calcularDesconto(pedido: Pedido): number {
  let desconto = 0;
  if (pedido.total > 100) {
    if (pedido.tipoUsuario === "vip") {
      desconto = pedido.total * 0.2;
    } else if (pedido.tipoUsuario === "premium") {
      desconto = pedido.total * 0.15;
    } else {
      desconto = pedido.total * 0.1;
    }
  } else if (pedido.itens > 5) {
    if (pedido.tipoUsuario === "vip" || pedido.tipoUsuario === "premium") {
      desconto = pedido.total * 0.05;
    } else {
      desconto = pedido.total * 0.02;
    }
  }
  return desconto;
}

Neste código, há uma série de condicionais aninhadas que aumentam a complexidade e dificultam a manutenção. Usando o padrão Early Return, podemos simplificar:

// Com early return e sem else
function calcularDesconto(pedido: Pedido): number {
  if (pedido.total > 100) {
    if (pedido.tipoUsuario === "vip") return pedido.total * 0.2;
    if (pedido.tipoUsuario === "premium") return pedido.total * 0.15;
    return pedido.total * 0.1;
  }

  if (pedido.itens > 5) {
    if (pedido.tipoUsuario === "vip" || pedido.tipoUsuario === "premium") {
      return pedido.total * 0.05;
    }
    return pedido.total * 0.02;
  }

  return 0;
}

Vamos ao breakdown:

  1. Simplificação do Primeiro Bloco if: No código original, a variável desconto é definida e modificada dentro de blocos if e else. Refatoramos para usar retornos antecipados (return) diretamente nas condições.

Primeira Mudança: Dentro do bloco if onde pedido.total > 100, substituímos o bloco else if e else por retornos diretos. Se pedido.tipoUsuario for “vip”, retornamos pedido.total * 0.2, se for “premium”, retornamos pedido.total * 0.15, e se for qualquer outro tipo, retornamos pedido.total * 0.1.

  1. Remoção de else: Eliminamos os blocos else que estavam dentro dos blocos if anteriores. Usar return diretamente evita a necessidade de else, simplificando a lógica.

  2. Tratamento do Segundo Caso: Se o primeiro bloco if não é atendido (pedido.total < = 100), o código verifica se pedido.itens > 5. Se esta condição for verdadeira, o desconto é calculado baseado no tipo de usuário.

Segunda Mudança: Similar à primeira refatoração, retornamos diretamente o valor do desconto com base na condição do tipo de usuário sem usar else.

  1. Retorno Padrão: Se nenhuma das condições anteriores for satisfeita, retornamos 0, que é o desconto padrão quando nenhuma condição é atendida.

Com isso temos:

Melhoria na Legibilidade — A eliminação de aninhamentos e do uso de else torna o código mais direto e mais fácil de seguir, com cada condição e sua ação correspondente claramente separadas.

Redução de Aninhamento — Ao usar retornos antecipados e evitar else, o código fica mais plano e menos complexo, facilitando a compreensão da lógica.

Facilidade na Manutenção — Com menos níveis de indentação e estrutura de código simplificada, é mais fácil modificar ou adicionar novas condições sem impactar a estrutura existente.

Menor Complexidade Ciclomática — Menos ramificações e condições aninhadas reduzem a complexidade ciclomática, o que pode facilitar a realização de testes e a manutenção do código.


Conclusão

Embora as declarações else tenham seu lugar na programação, descobri que evitá-las ativamente frequentemente leva a um código mais limpo e mais fácil de manter.

Lembre-se, o objetivo não é eliminar cegamente todas as declarações else, mas avaliar criticamente onde elas são realmente necessárias e onde estruturas alternativas podem levar a um código melhor.

Como em todas as práticas de programação, use seu julgamento e considere o que funciona melhor para seu contexto específico e equipe.