domingo, 13 de dezembro de 2015

Encapsulando implementações obrigatórias


Olá,

Hoje vou falar de algo bem simples, mas bastante comum no dia a dia dos desenvolvedores: os métodos obrigatórios das interfaces. Já é sabido que todos os métodos da interface devem ser sobrescritos, mesmo que você não escreva nenhuma linha de código no corpo desse método. Vamos pegar a interface MouseLinstener como exemplo:

public class MouseLst implements MouseListener {
 @Override
 public void mouseClicked(MouseEvent arg0) {}
 @Override
 public void mouseEntered(MouseEvent arg0) {}
 @Override
 public void mouseExited(MouseEvent arg0) {}
 @Override
 public void mousePressed(MouseEvent arg0) {}
 @Override
 public void mouseReleased(MouseEvent arg0) {}
}


Mesmo que você precise utilizar apenas um dos métodos acima, você precisará sobrescrever os outros deixando o corpo do método vazio ( { } ).

Alguns programadores, ao montar a interface gráfica do usuário (UI), estendem a classe JFrame, por exemplo, e implementam a interface MouseListener:

public class Tela extends JFrame implements MouseListener {
 @Override
 public void mouseClicked(MouseEvent e) {
 }
 @Override
 public void mouseEntered(MouseEvent e) {
 }
 @Override
 public void mouseExited(MouseEvent e) {
 }
 @Override
 public void mousePressed(MouseEvent e) {
 }
 @Override
 public void mouseReleased(MouseEvent e) {
 }
}

Depois disso passam a própria classe (this) como listener para o objeto:

Button b = new Button("Clique aqui");
b.addMouseListener(this);

A solução não é ruim, porém, caso você possua muitos botões na aplicação, isso deverá ser tratado no método:

 public void mouseClicked(MouseEvent e) {
  switch (((Button) e.getSource()).getName()) {
  case "button0":
   System.out.println("Clicou em botão 1");
   break;
  case "button1":
   System.out.println("Clicou em botão 2");
   break;
  case "button2":
   System.out.println("Clicou em botão 3");
   break;
  default:
   System.out.println(((Button) e.getSource()).getName());
  }
 }

Se você acompanha meu blog, já leu o post “Ifs demais...” e sabe o que eu penso sobre o uso do switch case, ou seja, não é recomendado.

Para evitar o uso do swtich case, poderíamos criar classes internas anônimas só que isso deixaria o código gigantesco pois, em cada classe interna, você teria que sobrescrever os métodos vazios da interface.

Então vamos à dica:

O Java permite que classes abstratas implementem interfaces, então, vamos criar uma classe abstrata Mouse que implementa MouseListener:


public abstract class Mouse implements MouseListener {

 public Button b;

 public Mouse(Button b) {
  this.b = b;
 }

 public abstract void mouseClicked(); // obrigatório sobrescrever

 // não obrigatório
 public void mouseEntered() {
 }

 @Override
 public void mouseClicked(MouseEvent arg0) {
  mouseClicked();
 }

 @Override
 public void mouseEntered(MouseEvent arg0) {
  mouseEntered();
 }

 @Override
 public void mouseExited(MouseEvent arg0) {
 }

 @Override
 public void mousePressed(MouseEvent arg0) {
 }

 @Override
 public void mouseReleased(MouseEvent arg0) {
 }
}

Perceba que eu encapsulo os métodos não utilizados dentro da classe e  passo os métodos não vazios para o evento desejado:

 public void mouseClicked(MouseEvent arg0) {
  mouseClicked();
 }

 @Override
 public void mouseEntered(MouseEvent arg0) {
  mouseEntered();
 }

Para facilitar nosso trabalho, eu adicionei um Button à classe caso seja necessário buscar o botão responsável pelo método chamado:


 public Button b;

 public Mouse(Button b) {
  this.b = b;
 }

Propositalmente, eu coloquei o método onClicked como obrigatório, para demonstrar que podemos dizer quais métodos queremos manter nessa condição:


public abstract void mouseClicked(); // obrigatório sobrescrever

Feito isso, você pode utilizar a classe abstrata Mouse de três formas:

1) Instanciá-la na passagem de parâmetro do método:

  // 1 -> instanciada no método
  Button b1 = new Button("Imprimir ...");
  b1.addMouseListener(new Mouse(b1) {
   @Override
   public void mouseClicked() {
    System.out.println(b.getLabel());
   }
  });

2) Classe interna anônima

  // 2 -> classe interna anônima
  Button b2 = new Button("Salvar ...");
  b2.addMouseListener(new SalvarClick(b2));
  jP.add(b2);


 public class SalvarClick extends Mouse {
  public SalvarClick(Button b) {
   super(b);
  }

  @Override
  public void mouseClicked() {
   System.out.println(b.getLabel());
  }
 }

3) Classe externa

  // 3 -> nova classe
  Button b3 = new Button("Salvar Como ...");
  b3.addMouseListener(new SalvarComoClick(b3));
  jP.add(b3);


public class SalvarComoClick extends Mouse {

 public SalvarComoClick(Button b) {
  super(b);
  // TODO Auto-generated constructor stub
 }

 @Override
 public void mouseClicked() {
  System.out.println(b.getLabel());
 }

 @Override
 public void mouseEntered() {
  System.out.println("Posicionou o mouse em [" + b.getLabel() + "]");
 }

}

Você pode inclusive, criar uma classe com todas as ações da tela e depois compartilhar essas ações entre os componentes. Isso facilita muito a manutenção do código.

Baixa o fonte e teste no seu computador.

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

Nenhum comentário:

Postar um comentário