terça-feira, 27 de maio de 2008

Enumerations e Hibernate. Parte 1 ou Das Motivações.

Refatorando um sistema no qual trabalho, deparei-me com uma situação semelhante a esta.

public class Genero{
private long id;
private String nome;
//gets e sets
}

public class Pessoa{
private long id;
private Genero genero;
//gets e sets
}

Onde Genero é uma entidade do meu domínio que é mapeada para uma tabela Genero no BD.
Sendo que esta tabela tinha registros com o campo NOME preenchido com o seguintes valores "MASCULINO" e "FEMININO".

Neste contexto, havia uma classe com o seguinte código:

if( "MASCULINO".equals( pessoa.getGenero().getNome()) ) ...faz alguma coisa

A saga começou com o fato da magic string "MASCULINO" me incomodar um bocado. Então resolvi criar uma constante a fim de tornar o código mais claro.

public static final NOME_GENERO_MASCULINO = "MASCULINO"

O que deixou o trecho de código da seguinte forma:

if( NOME_GENERO_MASCULINO.equals( pessoa.getGenero().getNome()) ) ...faz alguma coisa

Porém com mais algumas marteladas no código surgiu outra constante.

public static final NOME_GENERO_FEMININO = "FEMININO"

A partir do momento que havia estas duas constantes intimamente relacionadas, resolvi criar uma enumeration para encapsulá-las.

public enum NomeGenero{
MASCULINO,
FEMININO
}

Sendo que o código cliente ficou com a seguinte cara:

if( NomeGenero.MASCULINO.toString().equals( pessoa.getGenero().getNome()) ) ...faz alguma coisa

Bem ... Removi a magic string porém o código ficou maior. Mas o pior de tudo foi que, com a criação da enumeration, ficou claro que o conteúdo da coluna NOME da tabela GENERO estava replicado na enum NomeGenero. Ou seja, a mesma informação em duas "fontes de dados" distintas. BAD SMELL.

Daí contemplei a necessidade de haver somente uma "fonte de dados" e optei por manter a enumeration. As principais razões que me levaram a esta opção foram as seguintes:

1 - O código motivador de tudo isso ganharia clareza e type safety:

if( NomeGenero.MASCULINO == pessoa.getNomeGenero() ) ...faz alguma coisa

2 - Quando precisasse dos possíveis valores de gênero em vez de fazer um select na tabela Genero, o que me custaria processamento no SGBD, tráfego na rede e tudo o mais; bastaria fazer Genero.values() .

A principal razão que me afastaria da opção pela Enumeration é que quando surgisse um gênero novo eu teria que adicioná-lo na enum, para isso deveria alterar a fonte, recompilá-la e por fim realizar o deploy. Sendo que que na alternativa do BD, bastaria adicionar o registro novo na tabela GENERO. Porém, o fato de não surgir um gênero novo na raça humana corriqueiramente garante a estabilidade desta enumeration, o que praticamente anula esse trade off.

Assumindo o risco citado anteriormente, optou-se pela enumeration, o que implicou que a classe Genero e a tabela GENERO deixaram de existir e a classe Pessoa "Enumeration Powered" ficou da seguinte forma:

public class Pessoa{
private long id;
private NomeGenero nomeGenero;
//gets e sets
}

Porém para as coisas funcionarem desta forma o hibernate, que é o ORM que uso, deveria persistir o NomeGenero de Pessoa. Daí surgiram as seguintes questões; "O Hibernate persiste enumeration?"; "Como fazê-lo?"

...To be Continued

Nenhum comentário: