Como não aprender orientação a objetos: o excesso de ifs

2 views
Skip to first unread message

Rafael

unread,
Apr 12, 2011, 3:39:43 PM4/12/11
to Grupo SergInfo, Grupo TI-UNIT, Grupo UNIT-INFO, Grupo Calicomp, Grupo Dojo-SE, Grupo ForkInSergipe
 
 

Sent to you by Rafael via Google Reader:

 
 

via blog.caelum.com.br by Guilherme Silveira on 4/12/11

Aglomerados de 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;
 }
}

Esse tipo de condicional pode ser retrabalhado em objetos (ou funções dependendo da linguagem) que definam o comportamento a ser executado em cada caso. Herança e poliformismo podem (e na maioriam dos casos, devem) ser usados de maneira controlada para alterar um comportamento:

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;
 }
}

Nesse caso, os responsáveis pelo comportamento são encontrados de acordo com uma condição: o tipo que o objeto representa, sendo que cada subtipo pode redefinir o comportamento do método. Não haverá necessidade de ifs, já que essa descoberta de qual método executar é feita pela máquina virtual: funcionario.getBonus().

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 reflection. Note como, nesse caso, novamente, nenhum switch ou sequências de ifs precisam ser feitos: ifs são substituídos por polimorfismo:

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);
}

A invocação a 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.


 
 

Things you can do from here:

 
 

Wagner Macedo

unread,
Apr 12, 2011, 4:32:05 PM4/12/11
to forkin...@googlegroups.com, dojo-se
Muito bom, Rafael.

--
Wagner Macedo


2011/4/12 Rafael <rafael...@gmail.com>
Reply all
Reply to author
Forward
0 new messages