Como utilizar o padrão Strategy para implementar os métodos equals e hashCode?

2014/10/07

Utilizando o Strategy Design Pattern, é possível reduzir ainda mais a escrita de código para implementar os métodos equals e hashCode, sendo assim, disponibilizei em meu repositório a classe utilitária EqualityStrategy, que como o nome sugere, permite a criação de estratégias para verificar a equivalência de objetos.

Para relembrar, abaixo temos um exemplo padrão de implementação dos métodos equals e hashCode que levam em consideração cada atributo, este código é análogo aos algoritmos gerados pelas IDE's eclipse e NetBeans.

import java.util.Arrays;

class MinhaClasse {

    private int      atributo1;
    private Object   atributo2;
    private String[] atributo3;

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof MinhaClasse)) {
            return false;
        }
        MinhaClasse other = (MinhaClasse) obj;
        if (atributo1 != other.atributo1) {
            return false;
        }
        if (atributo2 == null) {
            if (other.atributo2 != null) {
                return false;
            }
        } else if (!atributo2.equals(other.atributo2)) {
            return false;
        }
        if (!Arrays.equals(atributo3, other.atributo3)) {
            return false;
        }
        return true;
    }

    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + atributo1;
        result = prime * result + ((atributo2 == null) ? 0 : atributo2.hashCode());
        result = prime * result + Arrays.hashCode(atributo3);
        return result;
    }
}

Percebam que o código exige bastante atenção quando for realizado uma manutenção, principalmente se a classe for alterada para possuir mais atributos que possam influenciar na computação do equals e hashCode.

Agora um exemplo de implementação de uma classe que utiliza o EqualsUtils e HashCodeUtils para computar o equals e hashCode.

import static br.com.staroski.equality.EqualsUtils.*;
import static br.com.staroski.equality.HashCodeUtils.*;

class MinhaClasse {

    private int      atributo1;
    private Object   atributo2;
    private String[] atributo3;

    public boolean equals(Object object) {
        if (this == object) {
            return true;
        }
        if (object instanceof MinhaClasse) {
            MinhaClasse that = (MinhaClasse) object;
            return equal(this.atributo1, that.atributo1)
                && equal(this.atributo2, that.atributo2)
                && equal(this.atributo3, that.atributo3);
        }
        return false;
    }

    public int hashCode() {
        int hash = MULTI_FIELD;
        hash = hash(hash, atributo1);
        hash = hash(hash, atributo2);
        hash = hash(hash, atributo3);
        return hash;
    }
}

Percebam que o código ficou bastante reduzido em relação aos algoritmos gerados pelas IDE's eclipse e NetBeans.

As implementações do equals e hashCode acima, podem ser simplificadas mais ainda, utilizando uma estratégia baseada nos atributos da classe.

Essa estratégia é obtida através da classe EqualityStrategy, disponível em meu repositório, veja o exemplo abaixo para entender:

import static br.com.staroski.equality.EqualityStrategy.*;

class MinhaClasse {

    private int      atributo1;
    private Object   atributo2;
    private String[] atributo3;

    private final EqualityStrategy estrategia = fieldBased(this);

    public int hashCode() {
        return estrategia.hashCode();
    }

    public boolean equals(Object object) {
        return estrategia.equals(object);
    }
}

Percebam que o tamanho do código reduziu drásticamente, o método fieldBased da classe EqualityStrategy, obtém uma estratégia que irá computar o equals e hashCode, utilizando os atributos do objeto passado como parâmetro, neste caso o this. Desta forma, ao sobrescrever os métodos equals e hashCode, basta delegar a chamada para o método da estratégia.