ifs aparecem com frequência, e chegam até
a ter um aspecto engraçado. Em alguns casos poder dar a impressão de
que estamos usando orientação a objetos, já que cada cláusula costuma
envolver a invocação de um método, dependendo do tipo do objeto.
Infelizmente, essa sensação é falsa, e chegou até a gerar o conhecido
movimento anti if campaign na internet.O exemplo a seguir mostra uma sequência de ifs que indicam condições distintas de execução:
public double calculaBonus(Funcionario f) { if (f.getCargo().equals("gerente")) { return f.getVendasDoMes() * 0.05 + getSalario() * 0.1; } else if (f.getCargo().equals("diretor")) { return f.getVendasDoMes() * 0.05 + getSalario() * 0.2 + (Today.isDecember() ? getSalario() : 0); } else { return f.getVendasDoMes() * 0.01; }} |
class Funcionario { // outros atributos e metodos aqui public double getBonus() { return vendasDoMes * 0.01; }}class Gerente extends Funcionario { // outros atributos e metodos aqui public double getBonus() { return vendasDoMes * 0.05 + getSalario() * 0.1; }} |
ifs, já que essa descoberta de qual método executar é feita pela máquina virtual: funcionario.getBonus().Esses ifs não costumam aparecer sozinhos. Assim como o comportamento
do bonus, surgem em breve outros comportamentos com os mesmos ifs em outras partes do sistema:
public double liberaVerba(Funcionario f, Produto produto) { if (f.getCargo().equals("gerente")) { return produto.getValor() < 5000 || produto.getTipo().equals(Tipo.URGENTE); } else if (f.getCargo().equals("diretor")) { return produto.getValor() < 10000; } else { return produto.getValor() < 1000 ||produto.getTipo().equals(Tipo.USO_DIARIO); }} |
comportamento deve ser alterada em diversos lugares distintos e, pior
ainda, é muito fácil esquecer um deles ao criar um novo comportamento:
alguns dos pontos de multiplos ifs são esquecidos. Ao escrever o mesmo código pela segunda vez, seguimos a prática sugerida no Pragmatic Programmers, de não nos repetir (DRY).
Mas o uso da herança é delicado, e o desenvolvedor deve estar ciente de que ela pode trazer um acoplamento indesejado e suas alternativas. O uso de interfaces se encaixaria aqui com perfeição.
Outros tipos de condições podem determinar qual ação deve ser tomada, como por exemplo o valor de um parâmetro, resultando em uma abordagem que utiliza um mapa. Note como, nesse caso, novamente, nenhum
switch ou sequências de ifs precisam ser feitos: ifs são substituídos por polimorfismo:private Map<String, Aplicador> taxas = new HashMap<String, Aplicador>();public void processa(String taxa, double juros) { impostosRecolhidos += taxas.get(taxa).aplicaComJuros(juros);} |
interface AplicadorDeTaxa { double aplicaComJuros(double valor); }}public void processa(String taxa, double juros) { Object instancia = Class.forName("br.com.caelum.taxas." + taxa).newInstance(); AplicadorDeTaxa aplicador = (AplicadorDeTaxa) instancia; impostosRecolhidos += aplicador.aplicaComJuros(juros);} |
Class.forName("br.com.caelum.taxas." + taxa).newInstance();
pode ainda ser encapsulada em uma Factory, que em vez de buscar por um
nome de classe, consultaria anotações ou um arquivo de configuração.Esses problemas com o
if surgem também em outros
paradigmas. Em linguagens funcionais é possível atingir o mesmo
resultado usando lambdas, ou ainda em procedurais é possível passar
ponteiros de funções com abstract data types.Além dos casos em que
ifs e condicionais podem ser
trocados pelo bom uso de polimorfismo, podemos seguir as boas práticas e
evitar condicionais complicados e muito aninhados.Fonte: blog.caelum.com.br
Nenhum comentário:
Postar um comentário