• Ramon Ferreira Silva

Armadilhas do Comando Switch

Atualizado: 26 de Ago de 2019


Armadilhas do Comando Switch

Esta duplicação estava em 3 locais diferentes e fazia sentido estar em cada um daqueles locais, mas não 3 ao mesmo tempo. Foi uma escolha difícil, saber onde manter e onde remover, no fim criei um nova classe para abstrair o problema e tudo foi resolvido.


Depois desse episódio, fiquei curioso e comecei a procurar switchs perdidos em outros projetos, e em quase todos eu encontrei algum tipo de problema.


Usando o comando Switch


É muito difícil escrever um comando switch que seja muito pequeno. Mesmo um switch com apenas dois cases, já acaba sendo maior do que deveria.


Dificilmente o comando switch faz apenas uma coisa o que o torna complexo e extenso. Infelizmente não podemos evitá-lo em todos os casos, mas o que podemos fazer é garantir que o switch fique isolado em uma classe de baixo nível e nunca repeti-lo fora daquela classe.Devemos usar o  polimorfismo para isso.


Violação de Princípios SOLID


Vejamos nosso código abaixo:

public float calcularRemuneracao(Funcionario funcionanrio) throws CargoInvalidoException {
    switch(funcionario.tipoCargo){

        case Cargo.COMISSIONADO:
                return calcularComissao(funcionario);
            break;
        case Cargo.ASSALARIADO:
                return calcularSalario(funcionario);
            break;
        case CARGO.ASSALARIADO_COM_COMISSAO:
                return calcularSalarioComComissao(funcionario);
            break;
        default:
            throw new CargoInvalidoException("Nao existe o tipo de cargo "  + funcionario.tipoCargo);
    }
}

Aqui temos vários problemas, o primeiro é que a função acaba sendo grande e crescerá mais ainda, o segundo é que ela viola o Princípio da Responsabilidade Única, o que nos deixa com mais de um motivo para fazer alguma alteração, terceiro ela viola o Princípio Abrerto/Fechado, pois cada vez que criarmos um novo tipo de cargo, precisaremos alterar essa função também. Quarto esta funçao conhece detalhes de implementção de muitas classes concretas diferentes, o que gera um alto nível de acoplamento. Quinto atiramos um exceção o que obriga qualquer classe que utilize esse método conhecer detalhes da implementação do método e da classe Cargo.


Uso Correto do Comando Switch


O jeito correto de resolver todos esses problemas, é realizando uma segregação de interfaces para poder tirar proveito do polimorfismo, através de uma Fabrica Abstrata de objetos que nos retornar a instancia correta da Interface Funcionário.

public interface Funcionario {
    boolean eDiaDoPagamento();
    float calcularRemuneracao();
}

public abstract class AbstractFuncionarioFactory(){
    abstract Funcionario criarFuncionario(Cargo);
}

public class FuncionarioFactoryImpl extends AbstractFuncionarioFactory throws CargoInvalidoException {
    
    public Funcionario criarFuncionario(Cargo cargo){
        switch(cargo.tipo){
            case Cargo.COMISSIONADO:
                return new FuncionarioComissionado();
            break;
        case Cargo.ASSALARIADO:
                return new FuncionarioAssalariado();
            break;
        case CARGO.ASSALARIADO_COM_COMISSAO:
                return new FuncionarioAsslariadoComComissao();
            break;
        default:
            throw new CargoInvalidoException("Nao existe funcionari com o cargo " + cargo);
        }
    }   
}

Com esta refatoração, conseguimos isolar o comando switch dentro de uma única classe, e agora qualquer local onde for preciso usar um Funcionário, não será necessário saber detalhes da implementação da classe. Separamos também a responsabilidade de calculo de remuneração e através do polimorfismo, podemos adicionar quantos novos cargos desejarmos.


Veja como ficou nosso método agora :

public float calcularRemuneracao(Funcionario funcionanrio) {
if(funcionario.eDiaDePagamento()){
return funcionario.calcularRemuneracao();
}

return 0.0f;
}


Conclusão


O comando switch deve ser usado com cautela, pois fica muito fácil gerar duplicação desnecessário no nosso código, além de aumentar significativamente o acoplamento. Seu uso isolado, dentro de classes de baixo nível, e usando o polimorfismo, ameniza em muito esse problema.

Até a próxima.

#CódigoLimpo #SOLID #Refatoração #PadrõesdeProjeto #BoasPráticas

2 visualizações