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