sexta-feira, 13 de novembro de 2015

IFs demais...


IFs demais...

Olá,

No post de hoje vou falar sobre o uso excessivo de “IF” e o uso de “SWITCH CASE” em orientação a objetos. Os programadores mais experientes sabem que essa prática é ruim e vai totalmente contra os princípios de POO.
O paradigma de orientação a objetos foi pensado de forma a permitir que os componentes respondam por si, pelo seu estado e que assumam características diferentes de acordo com sua implementação (polimorfismo).
Mas aí vem a pergunta: Quando devo usá-los então????
“Nunca”, talvez não seja a melhor resposta (para o uso do “switch case“ eu responderia nunca...), mas devemos usar com bom senso.
Vamos pensar na seguinte situação: Eu quero saber o nome de 5 crianças e a partir desses nomes comandar uma ação:

Pedro – Escovar os dentes;
Ana – Ir para a escola;
Marcelo – Pentear o cabelo;
Livia – Amarrar o tênis;
Taisa – Arrumar o quarto.

Se eu tivesse que resolver essa situação utilizando o IF, primeiro eu teria que perguntar quem é a criança, e em seguida comandar a ação:

Se você é o Pedro
Escove os dentes...
Se você não é o Pedro, mas é a Ana
Vá para a escola....
Se você não é a Ana nem o Pedro, mas é o Marcelo:
Penteie o cabelo...
E assim por diante...

Agora imagine-se diante das crianças se comportando dessa forma... fica estranho não acha? As próprias crianças iriam sugerir: “Você não quer saber o nosso nome, antes de nos dizer o que fazer?”... Isso vai economizar “TEMPO”.
E é exatamente esta a proposta da POO, facilitar as coisas para economizar tempo de processamento. Agora, usando POO, vamos resolver esta questão:

Primeiro eu sei que cada criança tem uma ação pré-definida, portanto, podemos criar uma classe abstrata “Crianca” e obrigar o programador a preencher essa ação:

abstract class Crianca {
 abstract void agir();
}

Agora, eu crio uma classe para cada criança, preenchendo a ação. Sempre que eu estender a classe “Crianca”, o compilador me obriga e preencher o método “agir()”.

public class Ana extends Crianca{
 public void agir() {
  System.out.println("Ir para escola...");
 }
}


public class Marcelo extends Crianca {
 public void agir() {
  System.out.println("Pentear o cabelo...");
 }
}
etc...

Depois de criar todas as classes, incluímos as crianças em uma lista e executamos seu método agir():

public class Mapa {
 public static void main(String[] args) {
  ArrayList<Crianca> criancas = new ArrayList<Crianca>();
  criancas.add(new Pedro());
  criancas.add(new Ana());
  criancas.add(new Marcelo());
  criancas.add(new Livia());
  criancas.add(new Taisa());  
  for (Crianca c : criancas) {
    c.agir();
  }
 }
}

Da mesma forma, eu poderia ter criado uma interface “Crianca” com o método agir e as classe com os nomes das crianças implementando essa classe.

Entenderam? Sem if, sem processamento desnecessário, só utilizando POO e fazendo com que os objetos façam aquilo que foi designado a eles na implementação dos seus métodos.

No post anterior, onde eu falo sobre animações em Android, eu propositalmente fiz uso de IFs para determinar qual animação seria adicionada à lista de animações. Perceba que, quando eu monto a ArrayList que vai ser enviada via intent, utilizo 4 IFs para terminar qual CheckBox foi selecionado e em seguida, mais 4 IFs para determinar qual animação veio do ArrayList para adicioná-las ao AnimatorSet.

Com isso, serão 8 IFs executados sempre que eu acionar o botão ANIMAR, e mais, sempre que eu quiser incluir na aplicação um CheckBox com mais um tipo de animação, terei que incluir um novo método na classe Animacao, um IF na classe MainActivity e mais um IF na classe AnimacaoActivity.

Esta solução está ruim pois dificulta a manutenção do código, sempre passa por todos os IFs mesmo que o usuário não selecione nenhum CheckBox e não utiliza os recursos de POO.

Vamos corrigir:

Primeiro, eu criei uma constante numérica para cada animação possível na minha classe Animacao:

public static int ANIM_ALPHA = 0;
public static int ANIM_TRANSLATEX = 1;
public static int ANIM_TRANSLATEY = 2;
public static int ANIM_ROTATE = 3;
public static int ANIM_SCALEX = 4;
public static int ANIM_SCALEY = 5;

em seguida, apaguei todos os métodos add(), criei um HashMap animators para receber as possíveis animações e as incluí com método loadMap():


private void loadMap() {
    animators.put(ANIM_TRANSLATEX, ObjectAnimator.ofFloat(view, "x", 100f, 10f, 100f));
    animators.put(ANIM_TRANSLATEY, ObjectAnimator.ofFloat(view, "y", 200f, 50f, 200f));
    animators.put(ANIM_ALPHA, ObjectAnimator.ofFloat(view, "alpha", 1f, 0f, 1f));
    animators.put(ANIM_SCALEX, ObjectAnimator.ofFloat(view, "scaleX", 1f, -1f, 1f));
    animators.put(ANIM_SCALEY, ObjectAnimator.ofFloat(view, "scaleY", 1f, -1f, 1f));
    animators.put(ANIM_ROTATE, ObjectAnimator.ofFloat(view, "rotation", 0f, 360f, 0f));
}


alterei o método executeAnimation(), para receber uma lista de inteiros indicando as animações escolhidas pelo usuário e executá-las:


public void executeAnimation(List<Integer> valores) {
    ArrayList<Animator> animacoes = new ArrayList<>();

    for (int i = 0; i < valores.size(); i++) {
        animacoes.add(animators.get(valores.get(i)));
    }

    AnimatorSet animSet = new AnimatorSet();
    animSet.playTogether(animacoes);
    animSet.setDuration(3000);
    animSet.start();
}

na classe MainActivity() mapeei os novos CheckBox e adicionei a todos eles um listener que preenche uma lista de inteiros que será enviada à classe AnimacaoActivity via intent.

Observem que foi utilizado um IF para determinar o estado do CheckBox e a partir deste estado incluir ou excluir um valor de animação da lista de inteiros. Neste caso, o IF será executado apenas 1 vez, caso o usuário marque ou desmarque o CheckBox, sem consumir processamento desnecessário:

private CompoundButton.OnCheckedChangeListener onCheckedChangeListener
        = new CheckBox.OnCheckedChangeListener() {

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if (isChecked)
            list.add((int) buttonView.getTag());
        else
            list.remove(list.indexOf(buttonView.getTag()));

    }

};

e por fim, na classe AnimacaoActivity, recebemos a lista de inteiros vinda do intent e passamos para o objeto animacao:


protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_animacao);
    animacoes = getIntent().getIntegerArrayListExtra("animacoes");

    img = (ImageView) findViewById(R.id.imageView);

    animaView();
}

public void animaView() {
    Animacao animacao = new Animacao(img);
    animacao.executeAnimation(animacoes);

}

Resolvido, sem IF, sem processamento desnecessário apenas com POO.
Para incluir uma animação, basta adicionar um novo CheckBox ao projeto, incluir uma constante para essa nova animação e adicioná-la ao mapa de animações da classe Animacao, tornando simples a manutenção do código.

Baixe o exemplo atualizado: Atualizado.7zip.
Baixe o exemplo das crianças para o Eclipse: Criancas.7zip

Por hoje é isso, até o próximo post.

Nenhum comentário:

Postar um comentário