Uma alternativa moderna e agnóstica para decisões condicionais baseada em mapeamento direto de comportamentos.
Aprenda como substituir estruturas "if" e "else" por Dicionários de Ações (maps), melhorando legibilidade, extensibilidade e aderência a boas práticas como o princípio Opem/Closed - com exemplos em várias linguagens.
Introdução
Em muitos projetos, especialmente os que crescem rapidamente, é comum vermos blocos de código cheios de condicionais do tipo if/else ou switch/case. Embora funcionem bem em casos simples, eles se tornam rapidamente difíceis de manter e testar conforme os cenários aumentam.
Mas... e se pudéssemos mapear diretamente valores a comportamentos, substituindo as condicionais por uma estrutura mais limpa, enxuta e elegante?
Neste artigo, vamos mostrar uma alternativa agnóstica ao uso de if e else, utilizando dicionários de ações - um padrão amplamente suportado em diversas linguagens de programação.
Abstraindo if e else com Dicionários de Ações: uma abordagem mais limpa, elegante e expansível
O problema: Condicionais que crescem descontroladamente.
Exemplo clássico:
Quantas vezes você já se deparou com trechos de código como este?
if tipo = 'A' then
ProcessaA
else if tipo = 'B'then
ProcessaB
else if tipo = 'C'then
ProcessaC
else
ProcessaPadrao;
Ou, em outras linguagens:
if tipo == 'A':
processa_a()
elseif tipo == 'B':
processa_b()
else:
processa_padrao()
Esse padrão parece inofensivo, mas conforme os casos aumentam, o código cresce em complexidade, fica difícil de manter, testar ou modificar. Uma pequena mudança pode exigir mexer em vários pontos do código, quebrando o princípio da responsabilidade única.
À medida que os tipos aumentam, a estrutura incha, viola o princípio da responsabilidade única, dificulta teste e torna o código mais frágil a mudanças.
Existe uma alternativa? Sim: Dicionários de Ações
Em vez de múltiplos if e else, podemos usar um mapa (ou dicionário) para associar valores a ações diretamente.
Imagine isso:
LDicionario := TDictionary<string, TProc>.Create;
LDicionario.Add('A', ProcessaA);
LDicionario.Add('B', ProcessaB);
LDicionario.Add('C', ProcessaC);
if LDicionario.ContainsKey(tipo) then
LDicionario[tipo]()
else
ProcessaPadrao();
acoes = {
'A': processa_a,
'B': processa_b,
'C': processa_c
}
acoes.get(tipo, processa_padrao)()
Veja como a estrutura fica mais limpas e fácil de manter. Se você quiser adicionar um novo tipo, basta registrar uma entrada no mapa.
Isso funciona em outras linguagens?
Sim! Quase todas as linguagens modernas oferecem uma forma de mapear chaves a ações (funções, procedimentos ou objetos executáveis).
Linguagem | Estrutura Equivalente | Suporte a Funções como Valor |
Python |
| ✅ funções como objetos |
JavaScript |
| ✅ funções são de 1ª classe |
Java |
| ✅ desde Java 8 com lambdas |
C# |
| ✅ |
Go |
| ✅ |
Ruby |
| ✅ |
C++ (moderno) |
| ✅ via |
const acoes = {
A: () => processaA(),
B: () => processaB(),
C: () => processaC()
};
(acoes[tipo] || processaPadrao)();
Map<String, Runnable> acoes = new HashMap<>();
acoes.put("A", () -> processaA());
acoes.put("B", () -> processaB());
acoes.put("C", () -> processaC());
acoes.getOrDefault(tipo, () -> processaPadrao()).run();
var acoes = new Dictionary<string, Action> {
{"A", ProcessaA},
{"B", ProcessaB},
{"C", ProcessaC}
};
if (acoes.ContainsKey(tipo))
acoes[tipo]();
else
ProcessaPadrao();
acoes := map[string]func(){
"A": processaA,
"B": processaB,
"C": processaC,
}
if acao, ok := acoes[tipo]; ok {
acao()
} else {
processaPadrao()
}Vantagens dessa abordagem
- Extensibilidade: adicione novos casos se, alterar a estrutura principal.
- Legibilidade: fácil de entender o que cada tipo faz.
- Reutilização: as funções são definidas separadamente, podendo ser testadas individualmente.
- Conformidade com princípios SOLID: especialmente o Open/Closed (aberto para extensão, fechado para modificação).
Fallback Elegante com Função Default
Como vimos nos exemplos, é fácil adicionar um comportamento padrão caso a chave não seja encontrada. Isso evita a necessidade de else, mantendo a lógica clara e explícita.
if not LDicionario.TryGetValue(tipo, LAcao) then
LAcao := ProcessaPadrao;
LAcao();acoes.get(tipo, processa_padrao)()Essa abordagem mantém o código enxuto mesmo quando há exceções.
Quando usar
- Quando você tem muitos casos condicionais simples baseados em valores.
- Quando deseja aplicar o princípio da responsabilidade única.
- Quando quer que seu código cresça sem virar um "monstro de if/else".
- Quando pretende evoluir para algo mais poderoso como "State" ou "Command Pattern".
Se você gostou dessa abordagem, saiba que ela pode ser expandida e integrada com padrões clássicos da orientação a objetos. Quer saber mais? Nos próximos artigos, vamos explorar:
- State Pattern: Como representar diferentes estados com comportamentos distintos, mapeando-os diretamente - ideal para máquinas de estado e fluxos interativos.
- Command Pattern: Como encapsular requisições como objetos, permitindo composição, fila, histórico e desfazer operações.
- Acoplamento e estruturas de plugin ou modularização: Como usar esse padrão para registrar ações em tempo de execução, criando sistemas flexíveis, plagiáveis e com comportamento por configuração.
Conclusão
Evitar if e else não é uma obsessão por estilo, mas uma forma de tornar o código mais modular, legível e pronto para crescer. usar dicionários de ações é uma ferramenta poderosa, suportada por praticamente todas as linguagens modernas, e funciona especialmente bem quando lidamos com comportamentos previsíveis baseados em valores.