Arquivo

Textos com Etiquetas ‘Java’

Usando AspectJ para injetar comportamento em um Enum

29, abril, 2010

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.

Renato Besen Arquitetura de Software

Testes parametrizados com JUnit

9, dezembro, 2009

Muitas vezes temos a necessidade de executar um mesmo teste repetidas vezes mudando apenas alguns parâmetros. A solução mais simples caso sejam poucos testes é replicar o método de testes e mudar as variáveis, ou então criar um método com as asserções desejadas e chamá-lo com os diferentes parâmetros.

Ambas as possibilidades apresentadas pecam, pois não respeitam o princípio DRY. Uma solução muito mais elegante é usar os testes parametrizados, uma possibilidade que para quem usa JUnit existe a partir da versão 4.

O funcionamento é simples: um método no teste, anotado com Parameters, deve retornar uma coleção de arrays, que serão os parâmetros de execução:

@Parameters
public static Collection<Object[]> parameters() {
    List<Object[]> list = new ArrayList<Object[]>();
    list.add(new Object[] {"asshole!", "jerk!"});
    list.add(new Object[] {"what the hell?", "what the heck?"});
    return list;
}

Cada array desta coleção fornecerá os argumentos para o construtor do teste, possibilitando assim que estes valores sejam salvos em campos da classe, e utilizados por quem precisar dos mesmos:

public ProfanityFilterTest(String input, String output) {
    this.input = input;
    this.output = output;
    filter = new ProfanityFilter();
}

A classe de testes deve ser configurada para rodar com um runner específico, o Parameterized:

@RunWith(Parameterized.class)
public class ProfanityFilterTest {
[...]

Com estes passos básicos, ao rodar a classe testes, cada teste será chamado uma vez com os parâmetros fornecidos.

Resultado dos Testes Parametrizados

Para conferir o código completo baixe ProfanityFilter.java e ProfanityFilterTest.java.

Renato Besen Arquitetura de Software, Auto-gestão

EclEmma: cobertura de código no Eclipse

13, abril, 2009

Para quem usa Eclipse, agora ficou mais fácil medir a cobertura de código dos testes automatizados.

O EclEmma é um plugin para o Eclipse que mede e apresenta a cobertura de testes no próprio Eclipse. Com o velho Emma ou com o brasileiro Cobertura, era preciso configurar o build para instrumentar o código e rodar a medição. Os resultados era visualizados em relatórios html. Nada realmente integrado à IDE!

Medir a cobertura com o EclEmma parece muito mais natural. Os testes são executados no próprio Eclipse e a apresentação dos trechos cobertos e descobertos é integrada ao editor Java.

Adriano Campestrini Ferramentas