Usando AspectJ para injetar comportamento em um Enum
Nota: Isso pode não ser viável na maioria dos casos, mas acho interessante pelo fator curiosidade.
Já me deparei algumas vezes em uma situação onde eu tinha uma série de enums que implementavam uma interface. Enums em Java (ainda) não podem herdar métodos, o que faz com que os métodos devam ser implementados em todos os enums, mesmo que o que a gente queira seja a mesma implementação pra todos os casos.
Um exemplo disso é o seguinte: para internacionalizar uma aplicação, por motivos que não vem ao caso, decidimos encapsular as mensagens em enums. Ou seja, teríamos uma interface Message, que todos os enums vão implementar, e cada contexto da aplicação teria seu próprio enum. Declaramos dois métodos em uma mensagem: um get(), que retorna a mensagem diretamente; um get(Object… args), que usa um MessageFormat para colocar os valores dos argumentos dentro da mensagem.
public interface Message {
String get();
String get(Object... args);
}
O método para recuperar as strings é o mesmo para todos os enums. Um ResourceBundle (que contém as mensagens carregadas de um arquivo properties) é acessado, e o valor correspondente ao valor do enum é retornado. Um enum típico seria assim:
public enum PersistenceMessage implements Message {
OPEN("persistence.open"),
SAVE("persistence.save");
private String key;
private PersistenceMessage(String key) {
this.key = key;
}
@Override
public String get() {
return Messages.get(key);
}
@Override
public String get(Object... args) {
return Messages.get(key, args);
}
}
O problema aqui é que os métodos em todos os enums vão estar duplicados.
Como seria bom se existisse um método de injetar comportamento nestes enums… talvez como os mixins do Ruby…
Acontece que em Java existe um jeito de fazer algo parecido com os mixins de Ruby. Com técnicas de AOP (aspect oriented programming), mas especificamente com ajuda do framework AspectJ, é possível injentar comportamento em classes com as chamados inter-type declarations.
Eu não vou explicar aqui o que é AOP ou como funciona o AspectJ, mas abaixo vai o exemplo de como fazer isso.
O primeiro passo é criar um aspecto para a interface Message, fornecendo os métodos desejados.
public aspect MessageGetter {
public String Message.get() {
return Messages.getString(this.getKey());
}
public String Message.get(Object... args) {
return Messages.getString(this.getKey(), args);
}
}
Como vocês devem ter percebido, eu adicionei na interface de Message o método getKey(), para ter um ponto de acesso à chave. Eu nunca usei AspectJ na prática, então talvez exista uma forma mais elegante de fazer isso. Essa foi a única alteração que fiz na interface, então não vou mostrá-la novamente.
Cada enum agora segue o seguinte padrão:
public enum PersistenceMessage implements Message {
OPEN("persistence.open"),
SAVE("persistence.save");
private String key;
private PersistenceMessage(String key) {
this.key = key;
}
@Override
public String getKey() {
return key;
}
}
O método getKey() passou a ser duplicado, mas acredito que seja muito menos grave do que a situação anterior.
Este foi um exemplo extremamente simples, e com certeza não é uma opção para todos depender do AspectJ só para esse comportamento em um projeto, mas acredito que só o fato de ter o conceito de mixins na cabeça seja útil para abrir alguns horizontes na hora de bolar a arquitetura de uma aplicação.



