Capítulo 6

Refatorando

6.1 - Analisando o código atual

Se olharmos para o nosso projeto agora, temos a seguinte situação:

Mas será que com o que temos agora podemos cadastrar produtos, conforme o cliente pediu?

Repare na última parte da pergunta, que é muito importante. Se entregarmos o que temos agora para o cliente, ele dirá que isso é inútil e nosso sistema não tem nada ainda.

O que faltou, que é muito importante, é integrar a parte visual (web) com as regras de negócios.

Mas repare que o jeito que estão nossas classes que lidam com um produto no banco de dados. Todo nosso código está em classes de testes, dentro do método main(). Se deixarmos dessa maneira ficaria muito difícil de utilizar esse código.

Então o que precisamos fazer é melhorar nosso código, para que possamos utilizar o que foi escrito dentro do main(), e também para melhorar a manutenção e a legibilidade.

6.2 - Refactoring

Uma prática bastante comum e difundida no meio da Orientação a Objetos é a chamada Refatoração (Refactoring). Refatorar um programa é melhorar seu código sem alterar sua funcionalidade. A ideia da refatoração não é corrigir bugs, por exemplo, mas melhorar a estrutura de seu código, deixá-lo mais OO, mais legível.

Há diversos tipos de refatorações. Renomear uma variável para um nome mais claro é um exemplo simples. Quebrar um método grande em vários métodos menores é um outro exemplo um pouco mais complicado.

Várias IDEs de Java possuem suporte à refatorações comuns. O Eclipse possui ótimas opções automáticas que utilizaremos nesse curso.

O livro mais famoso sobre refatorações foi escrito por Martin Fowler e chama-se Refactoring - Improving the Design of existing code. É um catálogo com dezenas de técnicas de refatoração e instruções de como e quando executá-las.

Agora é a melhor hora de aprender algo novo

Se você gosta de estudar essa apostila aberta da Caelum, certamente vai gostar dos cursos online que lançamos na plataforma Alura. Você estuda a qualquer momento com a qualidade Caelum.

Conheça a Alura.

6.3 - Aprendendo a refatorar

Vamos abrir novamente a classe de inserção de produtos.

package br.com.caelum.goodbuy.testes;

  // import's
  
  public class AdicaoDeProduto {

  public static void main(String[] args) {
    AnnotationConfiguration configuration = new AnnotationConfiguration();
    configuration.configure();

    SessionFactory factory = configuration.buildSessionFactory();
    Session session = factory.openSession();

    Produto produto = new Produto();
    produto.setNome("Prateleira");
    produto.setDescricao("Uma prateleira para colocar livros");
    produto.setPreco(35.90);

    Transaction tx = session.beginTransaction();
    session.save(produto);
    tx.commit();
  }
}

Olhando para o método main, podemos dividí-lo em três partes. Uma parte é a aquisição de uma Session, outra é a criação do produto, e a outra é adição do produto no banco de dados. Com base nessa divisão, vamos pedir para o Eclipse nos ajudar a refatorar, para fazer essa divisão de uma maneira segura e automática.

Apenas para ficar claro essa divisão, veja o código novamente, agora com os comentários mostrando onde começa cada parte.

 1 package br.com.caelum.goodbuy.testes;
 2 
 3 // import's
 4 
 5 public class AdicaoDeProduto {
 6 
 7   public static void main(String[] args) {
 8     // Aquisição da sessão
 9     AnnotationConfiguration configuration = new AnnotationConfiguration();
10     configuration.configure();
11 
12     SessionFactory factory = configuration.buildSessionFactory();
13     Session session = factory.openSession();
14 
15     // Criação do produto
16     Produto produto = new Produto();
17     produto.setNome("Prateleira");
18     produto.setDescricao("Uma prateleira para colocar livros");
19     produto.setPreco(35.90);
20 
21     // Adição do produto no banco de dados
22     Transaction tx = session.beginTransaction();
23     session.save(produto);
24     tx.commit();
25   }
26 }

Selecionando apenas a parte da aquisição da sessão, vamos utilizar as teclas de atalho do Eclipse para criar um método com essas instruções. As teclas de atalho são Alt + Shift + M, que representa a opção Extract Method.

Apertando essas teclas, o Eclipse mostrará uma tela perguntado o nome do método que será criado. O nome desse método será getSession().

Colocando o nome do método e apertando OK, o método será criado e a parte do código que usa essas instruções já muda automaticamente, fazendo a chamada ao método recém-criado.

6.4 - Exercícios

  1. Altere a classe AdicaoDeProduto, que está no pacote br.com.caelum.goodbuy.testes, colocando os comentários para deixar claro a divisão das partes.

     1 package br.com.caelum.goodbuy.testes;
     2   
     3 // import's
     4 
     5 public class AdicaoDeProduto {
     6 
     7   public static void main(String[] args) {
     8     // Aquisição da sessão
     9     AnnotationConfiguration configuration = new AnnotationConfiguration();
    10     configuration.configure();
    11 
    12     SessionFactory factory = configuration.buildSessionFactory();
    13     Session session = factory.openSession();
    14 
    15     // Criação do produto
    16     Produto produto = new Produto();
    17     produto.setNome("Prateleira");
    18     produto.setDescricao("Uma prateleira para colocar livros");
    19     produto.setPreco(35.90);
    20 
    21     // Adição do produto no banco de dados
    22     Transaction tx = session.beginTransaction();
    23     session.save(produto);
    24     tx.commit();
    25   }
    26 }
    
  2. Selecione as linhas que fazem a aquisição da sessão, conforme a figura abaixo:

    aprendendo-a-refatorar1.png
  3. Com as linhas selecionadas, pressione as teclas Alt + Shift + M (Extract Method), para criar um método com essas linhas. Após pressionar essas teclas, aparecerá a seguinte tela:

    aprendendo-a-refatorar2.png
  4. A tela que está sendo mostrada está esperando por um nome de método. Digite getSession como nome do método e pressione OK.

    aprendendo-a-refatorar3.png
  5. Note como o Eclipse mudou seu código, criando o método getSession e fazendo a chamada a este método. A classe deve estar assim:

    public class AdicaoDeProduto {
    
      public static void main(String[] args) {
        // Aquisição da sessão
        Session session = getSession();
    
        // Criação do produto
        Produto produto = new Produto();
        produto.setNome("Prateleira");
        produto.setDescricao("Uma prateleira para colocar livros");
        produto.setPreco(35.90);
    
        // Adição do produto no banco de dados
        Transaction tx = session.beginTransaction();
        session.save(produto);
        tx.commit();
      }
    
      private static Session getSession() {
        AnnotationConfiguration configuration = new AnnotationConfiguration();
        configuration.configure();
    
        SessionFactory factory = configuration.buildSessionFactory();
        Session session = factory.openSession();
        return session;
      }
    }
    
  6. Faça o mesmo para as linhas de criação de produto e adição de produto no banco de dados. No final, ficaremos com 3 métodos, um chamado getSession, que já foi feito, outro chamado criaProduto e outro chamado gravaProduto. Lembre-se de utilizar as teclas de atalho que vimos nesse exercício.

  7. Para conferência, ficaremos com a classe da seguinte forma:

     1 package br.com.caelum.goodbuy.testes;
     2   
     3 // import's
     4 
     5 public class AdicaoDeProduto {
     6 
     7   public static void main(String[] args) {
     8     // Aquisição da sessão
     9     Session session = getSession();
    10 
    11     // Criação do produto
    12     Produto produto = criaProduto();
    13 
    14     // Adição do produto no banco de dados
    15     gravaProduto(session, produto);
    16   }
    17 
    18   private static void gravaProduto(Session session, Produto produto) {
    19     Transaction tx = session.beginTransaction();
    20     session.save(produto);
    21     tx.commit();
    22   }
    23 
    24   private static Produto criaProduto() {
    25     Produto produto = new Produto();
    26     produto.setNome("Prateleira");
    27     produto.setDescricao("Uma prateleira para colocar livros");
    28     produto.setPreco(35.90);
    29     return produto;
    30   }
    31 
    32   private static Session getSession() {
    33     AnnotationConfiguration configuration = new AnnotationConfiguration();
    34     configuration.configure();
    35 
    36     SessionFactory factory = configuration.buildSessionFactory();
    37     Session session = factory.openSession();
    38     return session;
    39   }
    40 }
    

6.5 - Comentários são sempre necessários?

Note que o que fizémos foi quebrar um método grande em vários método pequenos. Pode parecer inútil esse tipo de alteração, já que não mudamos nada no código, só a forma como está organizado.

Mas repare que o método main ficou muito mais legível, intuitivo. Basta dar uma olhada rápida que podemos entender o que ele faz.

Mas agora que o método main foi "quebrado" em métodos menores, será que precisamos mesmo de comentários? Olhe novamente para o código, e veja que os nomes dos nossos métodos já dizem muito o que queremos, então nem precisamos mais de comentários.

Já que não precisamos mais dos comentários, vamos retirá-los do nosso código:

public class AdicaoDeProduto {

  public static void main(String[] args) {
    Session session = getSession();

    Produto produto = criaProduto();

    gravaProduto(session, produto);
  }
  // outros métodos
}

Você pode também fazer o curso FJ-28 dessa apostila na Caelum

Querendo aprender ainda mais sobre o framework VRaptor? Esclarecer dúvidas dos exercícios? Ouvir explicações detalhadas com um instrutor?
A Caelum oferece o curso FJ-28 presencial nas cidades de São Paulo, Rio de Janeiro e Brasília, além de turmas incompany.

Consulte as vantagens do curso Web ágil com VRaptor, Hibernate e AJAX.

6.6 - Refatorando para criar os DAOs

Olhando novamente para a classe de adição de produtos, podemos notar que ainda não podemos reaproveitar o código de criação de sessão do Hibernate em outras classes. O mesmo acontece com a classe de remoção de usuários.

Podemos então criar uma classe que vai ser responsável pela obtenção de Sessions, para que possamos usá-la sempre que precisarmos de uma. Vamos dar a essa classe o nome de CriadorDeSession, e mover o método getSession() para ela.

Da mesma forma, podemos juntar as operações de adicionar, remover, atualizar produtos em uma classe, que será responsável por acessar os produtos no banco de dados. E a essa classe vamos dar o nome de ProdutoDao.

6.7 - Exercícios

  1. Crie uma classe chamada CriadorDeSession no pacote br.com.caelum.goodbuy.infra. Essa classe será a responsável por criar uma Session.

  2. Selecione o método getSession da classe AdicaoDeProduto.

    refatorando-para-dao1.png
  3. Com as linhas selecionadas, pressione as teclas Alt + Shift + V (Move Method), para criar um método com essas linhas. Após pressionar essas teclas, aparecerá a seguinte tela:

    refatorando-para-dao2.png
  4. A tela que está sendo mostrada está esperando pela classe que ficará com esse método. Pressione o botão Browse..., e busque pela classe CriadorDeSession, conforme a imagem abaixo:

    refatorando-para-dao3.png
  5. Após encontrar a classe CriadorDeSession, pressione o botão OK para escolher essa classe, depois OK novamente para confirmar a mudança do método para essa classe.

  6. Aparecerá uma tela avisando que a visibilidade do método getSession será alterada public. Confirme essa alteração pressionando o botão Continue.

    refatorando-para-dao4.png
  7. Repare como ficou a classe após essa alteração:

    public class AdicaoDeProduto {
    
      public static void main(String[] args) {
        Session session = CriadorDeSession.getSession();
    
        Produto produto = criaProduto();
    
        gravaProduto(session, produto);
      }
    
      private static void gravaProduto(Session session, Produto produto) {
        Transaction tx = session.beginTransaction();
        session.save(produto);
        tx.commit();
      }
    
      private static Produto criaProduto() {
        Produto produto = new Produto();
        produto.setNome("Prateleira");
        produto.setDescricao("Uma prateleira para colocar livros");
        produto.setPreco(35.90);
        return produto;
      }
    }
    
  8. Crie uma classe chamada ProdutoDao no pacote br.com.caelum.goodbuy.dao. Essa classe será a responsável por encapsular as chamadas ao Hibernate.

  9. Mova o método gravaProduto para a classe ProdutoDao. Utilize as teclas de atalho que vimos nesse exercício.

  10. Repare como ficou a classe após essa alteração:

    public class AdicaoDeProduto {
    
      public static void main(String[] args) {
        Session session = CriadorDeSession.getSession();
    
        Produto produto = criaProduto();
    
        ProdutoDao.gravaProduto(session, produto);
      }
    
      private static Produto criaProduto() {
        Produto produto = new Produto();
        produto.setNome("Prateleira");
        produto.setDescricao("Uma prateleira para colocar livros");
        produto.setPreco(35.90);
        return produto;
      }
    }
    
  11. Vamos refatorar agora a classe ProdutoDao, que está no momento assim:

    public class ProdutoDao {
    
      public static void gravaProduto(Session session, Produto produto) {
        Transaction tx = session.beginTransaction();
        session.save(produto);
        tx.commit();
      }
    
    }
    

    Primeiro vamos tirar o static do método gravaProduto.

    public class ProdutoDao {
    
      public void gravaProduto(Session session, Produto produto) {
        Transaction tx = session.beginTransaction();
        session.save(produto);
        tx.commit();
      }
    
    }
    
  12. A classe AdicaoDeProduto agora não compila. Vamos trocar o acesso estático por acesso a uma instância do dao:

    public class AdicaoDeProduto {
    
      public static void main(String[] args) {
        Session session = CriadorDeSession.getSession();
    
        Produto produto = criaProduto();
    
        new ProdutoDao().gravaProduto(session, produto);
      }
    
      //...
    }
    
  13. O método gravaProduto recebe uma Session. Mas o ProdutoDao deveria ser responsável pelo acesso a dados, então ela mesma deve ser responsável por criar a session. Mude isso no ProdutoDao:

    public class ProdutoDao {
    
      public void gravaProduto(Produto produto) {
        Session session = CriadorDeSession.getSession();
        Transaction tx = session.beginTransaction();
        session.save(produto);
        tx.commit();
      }
    
    }
    

    Mude também a classe AdicaoDeProduto:

    public class AdicaoDeProduto {
    
      public static void main(String[] args) {
    
        Produto produto = criaProduto();
    
        new ProdutoDao().gravaProduto(produto);
      }
      //...
    }
    
  14. Por último, não precisamos criar uma Session dentro do método gravaProduto, vamos fazer isso dentro do construtor da classe, e colocar a Session como atributo:

    public class ProdutoDao {
    
      private final Session session;
    
      public ProdutoDao() {
        this.session = CriadorDeSession.getSession();
      }
    
      public void gravaProduto(Produto produto) {
        Transaction tx = session.beginTransaction();
        session.save(produto);
        tx.commit();
      }
    
    }
    
  15. Agora a chamada do método gravaProduto está correta, porém repare como está um pouco redundante:

    new ProdutoDao().gravaProduto(produto);
    

    Seria melhor se fosse assim:

    new ProdutoDao().salva(produto);
    

    Vamos fazer essa mudança de nome de método utilizando o Eclipse. Pressione as teclas Alt + Shift + R (rename) método da classe ProdutoDao, e o Eclipse pedirá o novo nome para o método.

    refatorando-para-dao5.png

    Digite salva como novo nome do método.

    refatorando-para-dao6.png
  16. (Opcional) Refatore as outras classes de teste, AlteracaoDeProduto e RemocaoDeProduto, para utilizar o ProdutoDao.

6.8 - Discussão em sala

Tire suas dúvidas no GUJ Respostas

O GUJ é um dos principais fóruns brasileiros de computação e o maior em português sobre Java. A nova versão do GUJ é baseada em uma ferramenta de perguntas e respostas (QA) e tem uma comunidade muito forte. São mais de 150 mil usuários pra ajudar você a esclarecer suas dúvidas.

Faça sua pergunta.