O controle de fluxo é uma das primeiras lições que aprendemos ao programar. Existem muitas formas de realizar esse controle, sendo o if a mais comum. Sua estrutura é bem simples:
1if condicao {
2 // Bloco executado se verdadeiro
3} else {
4 // Bloco executado se falso
5}
Dessa forma, controlamos o programa baseando-nos em expressões booleanas de maneira direta. A regra é clara: desviar o fluxo se algo é verdadeiro, caso contrário, executar o bloco else. Porém, nem sempre o else é importante ou obrigatório.
Você lê mais código do que escreve ¶
Esta é uma verdade fundamental na programação. Ao programar, entender o código existente é tão importante quanto criar novo código. Escrever código limpo não faz diferença para o computador, mas sim para quem lê.
Ter menos blocos else encadeados é uma forma de simplificar o fluxo sem alterar o comportamento, permitindo que outros programadores entendam facilmente a lógica. A sequência das instruções fica mais clara quando não precisamos “pular” mentalmente entre blocos condicionais.
1ok := false
2if ok {
3 // Este bloco não é executado
4} else {
5 // Executa aqui
6}
Lemos a linha 1 e em seguida a linha 5, o que adiciona complexidade no entendimento. A situação piora quando temos blocos if aninhados:
1ok := false
2ok2 := true
3if ok {
4 // Este bloco não é executado
5} else {
6 if ok2 {
7 // bloco aninhado!
8 }
9}
Agora temos múltiplos níveis de aninhamento, tornando o código muito difícil de entender! Através da supressão do else, podemos ter algo mais simples que expressa a mesma ideia funcionalmente.
É importante entender que podemos ignorar o else na maioria dos cenários, embora existam situações onde ele é mais útil.
Tudo se resume à quebra de fluxo ¶
A questão é que muitos cenários onde o else é utilizado não representam um desvio obrigatório de fluxo. Um exemplo claro é o seguinte:
1offset := 0
2if custom > 0 {
3 offset = custom
4} else {
5 offset = 10
6}
Neste caso, não é uma questão de controle de fluxo, mas sim de inicializar uma variável com um valor padrão de acordo com uma configuração. É muito código para algo simples. Podemos simplificar assim:
1offset := 10
2if custom > 0 {
3 offset = custom
4}
O comportamento não muda e fica muito mais direto de entender. No caso dos blocos aninhados:
1ok := false
2ok2 := true
3if ok {
4 // Este bloco não é executado
5}
6if ok2 {
7 // Executa aqui
8}
Agora ficou mais simples! Ao entender que o else não é necessário, melhoramos significativamente a legibilidade do código.
Early Return é seu amigo ¶
Outro padrão comum é o controle do retorno de função através do else:
1func shouldRender(page Page) string {
2 if page.Draft {
3 return page.Content
4 } else {
5 return ""
6 }
7}
Que pode ser reescrito como:
1func shouldRender(page Page) string {
2 if page.Draft {
3 return page.Content
4 }
5 return ""
6}
O else pode ser ignorado porque apenas o if precisa ser executado. Mesmo que haja um bloco mais extenso de código dentro do else, a refatoração terá o mesmo efeito. O early return é uma ferramenta muito útil para simplificações, pois geralmente o bloco else pode ser totalmente ignorado.
Falhe rápido ¶
Outro cenário comum é utilizar o padrão fail fast, ou seja, falhar cedo. Não vale a pena manter longos blocos se eles não serão executados em caso de falha.
1func shouldRender(page Page) error {
2 if err := Validate(page); err != nil {
3 return err
4 } else {
5 if err := RenderPage(page); err != nil {
6 return err
7 } else {
8 return nil
9 }
10 }
11}
O else está sendo usado para manipular o fluxo, tornando o código complexo. É como se o código estivesse tentando todos os caminhos, mas nem todos são válidos, pois não representam alterações de estado ou algoritmo. Seguindo a regra de remover os else, temos:
1func shouldRender(page Page) error {
2 if err := Validate(page); err != nil {
3 return err
4 }
5 return RenderPage(page)
6}
Agora a intenção fica mais clara e direta, sem indireções desnecessárias.
Então quando podemos usar o else? ¶
É claro que existem casos onde o else é essencial:
1if config.fileWriter {
2 // Caso tenha sido configurado
3 writeToFile(data)
4} else {
5 // Executa um writer padrão
6 writeToConsole(data)
7}
No exemplo acima, a escrita sempre é executada, utilizando diferentes estratégias. A escrita em arquivo é uma exceção e o padrão é utilizado para outros casos. O else é necessário porque faz parte do fluxo do programa como um todo.
Outros cenários onde o else faz sentido:
- Validação de dados: Quando você precisa tratar casos válidos e inválidos de forma diferente
- Configurações alternativas: Quando diferentes configurações levam a comportamentos distintos
- Lógica de negócio complexa: Quando a lógica realmente requer dois caminhos mutuamente exclusivos
Benefícios de evitar o else ¶
- Código mais legível: Menos níveis de aninhamento
- Menos complexidade ciclomática: Reduz a complexidade do código
- Mais fácil de testar: Menos caminhos de execução
- Melhor performance: Menos saltos condicionais
- Código mais funcional: Segue princípios de programação funcional
Concluindo ¶
Programar é um ato não só de instruir o computador, mas de comunicação. Existem diferentes formas de executar operações, e sempre devemos preferir as mais diretas e com menos controle desnecessário.
O else não é um vilão, mas na maioria dos casos é desnecessário. Aprender a identificar quando ele pode ser removido é uma habilidade valiosa que melhora significativamente a qualidade do seu código.
Lembre-se: código limpo é código que comunica claramente sua intenção. Às vezes, menos é mais.