Versão ALPHA! Este artigo está em versão 'Alpha' e, portanto, não foi ainda revisado corretamente

Implementando Keys

Introdução

Algumas vezes ao implementar um buscador, consulta ou até mesmo cache é necessária a utilização de classes conhecidas como Keys que contém uma ou mais propriedades de uma classe em particular. Mas o que são keys e por que elas existem? Keys são classes criadas que garante a unicidade e a imutabilidade de determinada informação e em caso de uma busca, é possível garantir que o resultado da busca será o resultado esperado. São criadas quando uma determinada classe não implementa Equals e HashCode, e esta classe precisa ser utilizada como chave por exemplo em um cache, é necessário criar uma key.

Acesso rápido

Para acessar os tópicos do artigo siga o checklist abaixo:

Criando sua Key help!
Implementando o teste de sua key help!
Erro comum help!

Criando sua Key

As classes Key geralmente permanecem no mesmo pacote que a entidade a qual ela se refere, se a classe ClienteKey foi criada, logo a mesma deve se encontrar no mesmo pacote que Cliente.

Antes de criar sua key é preciso saber quais atributos podem compor a classe. Atributos que podem compor uma key são todos aqueles que conseguem garantir unicidade, e geralmente este atributo é o id, só que pode acontecer do id não ser suficiente para que isso aconteça e aí é necessário criá-la adicionando outros atributos. Quais atributos podem ser utilizados? Abaixo há um exemplo de possíveis candidatos a comporem uma key:

  • código (enum) + id
  • id (da interface atual) + id ( de uma interface herdada)
  • tipo (enum) + código (string) + nome (string)

A utilização de outros atributos na criação de uma key é análoga a utilização de dois ou três atributos para criação de uma chave primária composta. Obs.: As keys não precisam ser compostas necessariamente de chaves primárias.

Em nosso exemplo vamos implementar ClienteKey que será composta por código, mãos à obra.

public class ClienteKey {

  private final String codigo;

  public ClienteKey(String codigo) {
    this.codigo = codigo;
  }

  public String getCodigo() {
    return codigo;
  }

  @Override
  public final int hashCode() {
    return Objects.hashCode(codigo);
  }

  @Override
  public final boolean equals(final Object obj) {
    if (obj == this) {
      return true;
    }
    if (obj == null) {
      return false;
    }
    if (obj instanceof ClienteKey) {
      final ClienteKey that = (ClienteKey) obj;
      return Objects.equal(this.codigo, that.codigo);
    } else {
      return false;
    }
  }

}

Obs.: Se o tipo de dados passado para a construção da key for um tipo primitivo, utilizar == ao invés de equal, evitando assim que a jvm faça autoboxing desnecessário. Exemplo:

 if (obj instanceof ClienteKey) {
      final ClienteKey that = (ClienteKey) obj;
      return Objects.equal(this.codigo, that.codigo);
 }

 if (obj instanceof ClienteKey) {
      final ClienteKey that = (ClienteKey) obj;
      return this.id == that.id;
 }

Para este último exemplo suponha que o id é um int. <div class="alert alert info"> Cuidado! Atente que ao gerar os métodos equals() e hashCode() é preciso defini-los como final, para evitar que os mesmo sejam sobrescritos, o eclipse por padrão não faz isso! Se os métodos não forem definidos como final irá aparecer esta mensagem: </div>

FAILED: equals_test java.lang.AssertionError: Subclass: equals is not final. Supply an instance of a redefined subclass using withRedefinedSubclass if equals cannot be final.

Para verificar se de fato tudo está correto é preciso implementar um teste de ClienteKey que verificará se a implementação dos métodos acima está correta.

Implementando o teste de sua key

Como de costume o teste deve permanecer no mesmo pacote de testes que a classe ClienteKey, atente-se a implementação do teste.

@Test
public class TesteDeClienteKey {

   public void equals_test() {
	EqualsVerifier
	    .forClass(ClienteKey.class)
	    .verify();
   }

}

Erro comum

Um erro comum que acontece no teste de key é o de mutabidade, em implementações mais antigas as variáveis não eram definidas como final no Jdbc. Para melhor entendimento será utilizado o Jdbc da entidade Cliente.

public class ClienteJdbc implements Cliente {

  private Integer id;

  private String codigo;

  private String nome;

  public ClienteJdbc() {
  }

  public ClienteJdbc(String codigo, String nome) {
    this.codigo = codigo;
    this.nome = nome;
  }

  public ClienteJdbc(Construtor construtor) {
    this.codigo = construtor.getCodigo();
    this.nome = construtor.getNome();
  }

O teste de cliente foi montado muito parecido com o TesteDeClienteKey, com a diferença de ser passado o Jdbc:

@Test
public class TesteDeCliente {

  public void testeDeEquals() {
    EqualsVerifier
	.forClass(ClienteJdbc.class)
	.verify();
  }

}

Ao executar o teste dará um erro de mutabilidade com esta mensagem no console:

FAILED: testeDeEquals java.lang.AssertionError: Mutability: equals depends on mutable field codigo.

Para corrigir este erro deve-se colocar o enum suppress Warning e o teste ficará desta forma:

@Test
public class TesteDeCliente {

  public void testeDeEquals() {
    EqualsVerifier
	.forClass(ClienteJdbc.class)
	.withRedefinedSuperclass()
	.withRedefinedSuperclass().suppress(Warning.NONFINAL_FIELDS).verify();
  }

}

Após este tratamento o teste funcionou. Com a utilização deste enum o teste ignora campos que não são final. Para entender mais sobre os funcionamento do EqualsVerifier clique aqui.


 
 

objectos, Fábrica de Software LTDA

  • R. Demóstenes, 627. cj 123
  • 04614-013 - Campo Belo
  • São Paulo - SP - Brasil
  • +55 11 5093-8640
  • +55 11 2359-8699
  • contato@objectos.com.br