segunda-feira, 9 de setembro de 2013

Testando Parâmetros e Estados de Objetos com Guava

Quando criamos classes em Java, um dos pontos mais importantes é proteger o estado dos objetos que serão ou estão instanciados. Uma das maneiras de se atingir este objetivo é colocar infindáveis estruturas “if/throw” para testar os parâmetros de construtores e métodos, dificultando a legibilidade de nosso código. Como no construtor abaixo:    
public Livro(String titulo, int paginas) throws IllegalArgumentException {
   if (titulo == null)

    throw new IllegalArgumentException("titulo não pode ser nulo");

   if (titulo.length() < 0)

    throw new IllegalArgumentException("O tamanho de titulo deve ser maior que zero");

   if (paginas < 0)

    throw new IllegalArgumentException("paginas deve ser maior que zero");
  this.titulo = titulo;

  this.paginas = paginas;

}

Estas estruturas ficam espalhadas por todo nosso código. Podemos até criar métodos específicos que façam estes testes, mas então o tamanho das classes aumentam cada vez mais.


Para facilitar nosso trabalho e manter o código limpo, podemos utilizar a biblioteca Guava. Para quem não conhece, Guava é um biblioteca que fornece várias classes utilitárias e é desenvolvida pelo Google. Guava segue vários conceitos do livro “Effective Java” de Joshua Bloch, que aliás trabalhou na arquitetura do projeto. Além disso, o Google utiliza a biblioteca em seus próprios projetos Java, que com certeza traz confiança na qualidade da mesma.

Na biblioteca encontramos a classe Preconditions, que pode facilitar os testes de argumentos e estados de objetos, tornando nosso código mais limpo e bonito. A classe Preconditions possui seis métodos, sendo que todos são estáticos. Desta forma pode-se usar “import static” para facilitar o trabalho. Os métodos e as exceções que estes lançam são:

Método
Exceção Lançada
checkArgument(boolean)
IllegalArgumentException
checkNotNull(T)
NullPointerException
checkState(boolean)
IllegalStateException
checkElementIndex(int index, int size)
IndexOutOfBoundsException
checkPositionIndex(int index, int size)
IndexOutOfBoundsException
checkPositionIndexes(int start, int end, int size)
IndexOutOfBoundsException

Um método muito interessante é o checkArgument(), que testa uma expressão booleana e lança IllegalArgumentException caso esta for falsa.
 
checkArgument(paginas > 0,"O valor de paginas(%s) deve ser maior que zero", paginas);
 
Neste caso foi utilizado o método sobrecarregado que aceita como parâmetro uma String formatável para mostrar uma mensagem.
Outro exemplo é o checkNull, que verifica se o argumento passado é nulo. Podemos atribuir o valor do parâmetro ao campo diretamente, pois o retorno do método é o próprio valor que foi testado:
 
this.titulo = checkNotNull(titulo, titulo não pode ser nulo);

Caso o parâmetro for nulo, NullPointerException será lançada e a mensagem será exibida.

O método checkState avalia se o estado do objeto é correto ao se chamar um determinado método. Você pode imaginar uma classe Builder, com vários métodos “set” para os campos que serão utilizados para construção de um objeto. Ao charmar o método build(), alguns campos não podem ter determinado valor ou null. Exemplo simplificado:

import static com.google.common.base.Preconditions.*;

public class ExemploBuilder {

  private int tamanho;

  private String nome;

  public ExemploBuilder setTamanho(int tamanho) {

   this.tamanho = tamanho;

   return this;

  }

  public ExemploBuilder setNome(String nome) {

   this.nome = nome;

   return this;

  }

  public Exemplo build() throws IllegalStateException {

   checkState(tamanho > 0,

   "deve-se fornecer tamanho com valor maior que zero");

   checkState(nome != null, "nome não pode ser nulo");

   return new Exemplo(this.tamanho, this.nome);

  }

  class Exemplo {

    private int tamanho;

    private String nome;

     public Exemplo(int tamanho, String nome)   {

      this.tamanho = tamanho;

      this.nome = nome;

    }

  }


  public static void main(String[] args) {
   //lançará uma exceção.

   new ExemploBuilder().setTamanho(5).build();  

  }

} 
O checkState não avalia os parâmetros do método em si, mas se o estado do objeto é seguro ao se chamar o método. Outro exemple, seria um objeto resultSet, que é retornado após uma consulta em um banco de dados. Os métodos “get” só podem ser chamados após o método next() ser invocado.

O método checkElementIndex verifica se o valor de um índice é válido para um array, string ou outra lista qualquer. Como parâmetro, ele requer o valor do índice e o tamanho da lista. Veja o exemplo: 
int[] a = {5,8,9,4,6};
checkElementIndex(4, a.length);

Este foi um artigo demonstrando alguns métodos da classe Preconditions do Guava. O Guava possui muitas outras utilidades interressantes que podem ser consultadas na página do projeto ou também no Wiki.

Este artigo de Markus Sprunk fala de Preditons e outros métodos de verificação de argumentos em Java, fazendo uma comparação interessante.

Até mais.

Nenhum comentário:

Postar um comentário