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:
-
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.
-
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.
-
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.
-
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:
- 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.
-
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.
-
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.
- 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.