Mirror DSL: facilitando o uso da API de reflection

Por Jonas Abreu em 17/11/08

No último domingo foi feito o primeiro release público do projeto Mirror (versão 1.2).

O Mirror é um projeto que tem por objetivo facilitar o uso da Java Reflection API, removendo boa parte da burocracia (como as diversas checked exceptions que são lançadas) e utilizando uma DSL para melhorar a legibilidade do código.

Com essa remoção de burocracia e a DSL, é possível transformar o seguinte código:

Field toSet = null;
for (Field f : target.getClass().getDeclaredFields()) {
    if (f.getName().equals("field")) {
        toSet = f;
    }
}
if (toSet != null && ((toSet.getModifiers() & Modifier.STATIC== 0)
        && ((toSet.getModifiers() & Modifier.FINAL== 0)) {
    toSet.setAccessible(true);
    toSet.set(target, value);
}

em algo mais legível e expressivo:

Mirror.on(target).set().field("fieldName").withValue(value);

Atualmente o Mirror possui suporte para lidar com as operações reflectivas mais comuns (como instanciar objetos, invocar métodos, ler ou escrever atributos, etc). Ele foi desenvolvido por Adriano Almeida, Diego Feitosa e eu, todos consultores/instrutores aqui da Caelum, enquanto enfretavamos problemas comuns no dia a dia.

Esperamos que possa ser útil para vocês também!

Domain-Driven Design no Falando em Java 2008

Por Sérgio Lopes em 26/05/08


No Falando em Java 2008, apresentei uma palestra introdutória sobre Domain-Driven Design. Apesar do tempo curto, os comentários foram ótimos! Muito obrigado a todos os que comentaram: pessoas no evento, blogs e GUJ. Falar de DDD em 40 min foi meu maior desafio e acabou faltando um pouquinho de tempo no final, mas deu para passar a mensagem.

Domain e Ubiquitous Language

O ponto fundamental do DDD é o primeiro D, o Domain. Tudo gira em torno desse tal de Domínio. O domínio é, em poucas palavras, o problema que queremos resolver com o programa que estamos desenvolvendo. Alguém (um cliente) tem um problema na área de atuação dele (geralmente nada a ver com informática) e contrata uma equipe de programação para ajudá-lo (nós :).

Segundo o DDD, é impossível resolver esse problema satisfatoriamente sem entender direito o que acontece no domínio do cliente. Não basta os desenvolvedores saberem mais ou menos: é necessário entrar fundo no domínio do cliente.

Mas é claro que nosso objetivo não é se tornar um especialista completo na área do cliente, mas apenas compreendê-la. A palavra-chave para isso acontecer é Conversa. Conversa constante e profunda entre os especialistas de domínio e os desenvolvedores.

Aqueles que conhecem o domínio em detalhes devem conversar com aqueles que conhecem programação em detalhes. Juntos, tentarão chegar a uma língua comum em que todos consigam se entender e que será usada em todas as conversas. É o que o DDD chama de Ubiquitous Language: uma língua baseada nos termos do domínio, não totalmente aprofundada neste, mas suficiente para descrever o problema satisfatoriamente.

Construção do Domain Model

Durante a conversa constante, todos juntos chegarão a um consenso sobre o Domínio. Os especialistas de domínio eventualmente criarão simplificações para facilitar a conversa; e os desenvolvedores podem introduzir conceitos técnicos simples.

Com isso, todos criam um modelo do domínio. É uma abstração do problema real, desenvolvida em conjuntos pelos especialistas do domínio e desenvolvedores. No DDD, é chamado de Domain Model.

É esse modelo que os desenvolvedores vão implementar em código. Literalmente. Item por item, como foi acordado por todos. Será desenvolvido um código limpo, com palavras do domínio, que representa, na programação, o domínio em discussão.
Foto do Sérgio no FJ2008
Usando DDD, seu programa orientado a objetos deve expressar a riqueza do domain model. Qualquer mudança no modelo (e, acredite, isso é muito comum) deve ser refletida imediatamente no código. Se algo do modelo torna-se inviável de se implementar tecnicamente, não se faz um “ajustezinho” no código; o modelo deve ser mudado para ser mais fácil de se implementar.

Ou seja, sempre seu código será expressão do modelo, que por sua vez é baseado totalmente no domínio.

Implementando o Domain Model

O DDD define uma série de patterns para facilitar a implementação do modelo em código. Mas, com absoluta certeza, esse não é o ponto principal do DDD. São apenas ferramentas que facilitam essa implementação.

Na palestra, mostrei alguns patterns de forma bem simples e rápida, como Entity e Value Object. E mostrei o tão discutido, debatido e mal-compreendido Repository.

O cliente descreve ao desenvolvedor o seguinte problema: “preciso saber todos os peixes que são da cor azul”. (na palestra, usei o exemplo de uma loja de peixes) Para o cliente, é natural em seu domínio, que se consiga “buscar” coisas. A idéia é recuperar “objetos” do domínio (entities) previamente conhecidos, baseado eventualmente em algum critério.

A noção de repositório surge justo dessa necessidade: chegar nos objetos de conhecimento do domínio. Na palestra, eu levantei a questão de que o nome repositório não deve ser algo interno ao código, mas deve fazer parte da Ubiquitous Language, deve aparecer nas conversas e no Domain Model. Ou seja, repositório deve ser um conceito que o especialista de domínio também entende e, por que está no Model, é ele que vai para o código.

Não há problema em trazer palavriado técnico para a Ubiquitous Language, desde que o príncipio da UL seja mantida: todos entendem o conceito. E, se, eventualmente, no contexto do domínio sendo tratado, outro nome faça mais sentido que repositório, esse nome deve ser usado (mesmo que nós técnicos saibamos que no fundo aquilo é um repositório).

Repositório como interface? Classe concreta delegando? DAO implementa Repository?
Tanto faz. Um outro ponto fundamental do DDD é: nada tem resposta definitiva. Se você entende a questão toda do Domain Model e aplica essa noção na programação, pode usar diversas formas diferentes de implementar tudo isso.

Na palestra, eu representei o Repository como uma interface dentro do Model. E a implementação (que, do ponto de vista do DDD, não importa) era um DAO com Hibernate na camada de infraestrutura.

Concluindo

Meu ponto principal na palestra foi mostrar a Ubiquitous Language e o Domain Model, que são o coração do DDD. Vou escrever um segundo artigo com códigos e mais comentários da palestra, mas paro esse artigo gigante por aqui.

Termino linkando para um excelente post do Philip Calçado que ele publicou essa semana (parece até que combinamos) sobre DDD falando justo que o que conta no DDD é o Domínio e não os Patterns. Ele conta uma historinha de um projeto onde todos “entendiam” DDD, usavam Repositórios, Entities etc, mas infelizmente não falavam a mesma língua do domínio.

JustJava 2007, Arquitetura e Caelum

Por Paulo Silveira em 08/10/07

Neste último JustJava palestrei juntamente com o Phillip Calçado a respeito das novidades em relação a arquitetura Java. Aproveitei para extrair conhecimento e idéias do Phillip, que sempre anda muito ligado com as novidades.

DSC00334 DSC00359

Como a apresentação é bem sucinta, vale falar um pouco sobre ela. O intuito foi mostrar as arquiteturas e designs enlatados e que durante muito tempo reinaram por aí, seja na forma de Core J2EE patterns mal aplicados ou no mal uso de clusters. O início da palestra mostra diversos pontos do nosso cotidiano de décadas passadas: destaque para os design patterns ValueObject (na realidade TransferObject), BusinessDelegate e ServiceLocator. Esses design patterns faziam muito sentido quando no J2EE os entity beans não eram serializáveis, e sim acessados remotamente, além de que não existia injeção de dependência.

Apesar do Java EE 5.0 trazer essas novidades, muita gente acaba aplicando esses design patterns sem nenhuma necessidade, o que acaba criando o padrão carinhosamente chamado de BOLOVO pelo Phillip: classes como UsuarioBusinessObject (BO), UsuarioLayerObject (LO), UsuarioValueObject (VO), UsuarioXYZ, etc.

Toda vez que uma classe de domínio é criada em um sistema como esses, suas irmãs também aparecem: BOs, VOs, LOs, XYZs. Utilizar TOs apenas para transportar objetos entre camadas (e não tiers) não faz sentido algum, polui o código e diminui flexibilidade e manutenção. Separar funcionalidade e dados entre BOs e VOs é outro grande problema: onde está a orientação a objetos? Entity beans apenas com getters e setters é um mau sinal.

Passamos por Model Driven Design, dando alguns exemplos com código de Domain Driven Design e de Domain Specific Languages. Por último atacamos SOA, comparando o modelo WSDL/SOAP com o Plain Old XML (POX), além de outras alternativas. Cada forma de webservices, seja SOAP, POX ou JSON, tem seu caso de uso. O que mostramos na palestra foi que não deve-se optar diretamente por SOAP sem antes pensar bem nas necessidades para aquele serviço. O mesmo para os design patterns, práticas e idéias aqui discutidos ou até mesmo criticados: cada um tem seu lugar, não utilize-os apenas porque estão em um livro ou já foram muito usados em prévias arquiteturas.

No final falamos que não há uma arquitetura enlatada que possa resolver todos nossos problemas, e que devemos tomar muito cuidado para não construir um monstro para resolver um pequeno sistema. Cada projeto deve ter sua arquitetura muito bem estudada, com todas as suas particularidades.

Na sexta feira ainda tivemos uma palestra de Lucene apresentada pelo Guilherme Moreira. Aproveitando a ocasião, este domingo ocorreu uma confraternização e reunião da Caelum para discutir idéias, projetos e metas.

DSC00377 DSC00389
Time Caelum Out/2007 faltando 3 pessoas DSC00392

Tivemos novos participantes, os mais novos integrantes e colaboradores da Caelum: Jonas Abreu, Cecília Fernandes, Alexandre Magno, Danilo Sato e Ricardo Nakamura! O time está crescendo. Nas fotos faltam apenas o Carlos Felício, Suellen Campana e o Rafael Consentino.

Domain Specific Languages em ação

Por Paulo Silveira em 21/09/07

Em diversos momentos sentimos a necessidade de utilizar uma linguagem para atacar um problema mais específico. Utilizar Java ou C# nesse tipo de problema pode gerar uma enorme quantidade desnecessária de código. Veja um exemplo que passamos na Caelum:

Set<Strategy> strategies = new HashSet<Strategy>();
Indicator<Double> close = new ClosePriceIndicator(timeSeries);
for (int i = 1; i <= 50; i++) {
  Indicator<Double> tracker = new EMAIndicator(close, i);
  Strategy strategy = new 
    IndicatorCrossedIndicatorStrategy(close, tracker);
  strategies.add(strategy);
}

No nosso caso, esse trecho de código deve ser compreensível para analistas de negócio, que não são necessariamente programadores, muito menos possuem conhecimento de Java.

Domain Specific Languages é o nome dado a prática de se criar pequenas linguagens para resolver um problemas bem específicos. Elas existem em dois sabores: as externas, que criam uma linguagem própria, e as internas, que na verdade utilizam um subconjunto de instruções de um linguagem já existente e utilizada no sistema.

Entre alguns clássicos exemplos de DSLs internas temos o uso da Criteria do hibernate, o uso do ruby nos arquivos de build do rake. Entre as DSL externas, temos as macros do excel e o xml do ant.

Utilizando a api de scripting do java 6, passamos a usar ruby (através da JRuby) para escrever essa parte da lógica de negócios, e nosso código em java ficou assim:

(1..50).collect{|x|
	Tail::IndicatorCrossedIndicatorStrategy.new(close,
		Tail::EMAIndicator.new(close, x))
}

Criando algumas factories, conseguimos chegar a um código muito mais simples:

(1..50).collect{|x|
	cross(close, ema(x))
}

Logo, estamos próximos de chegar a algo parecido com uma linguagem natural:

x de 1 a 50
       quando cruzar (fechamento, ema(x))

Para tal, uma das possibilidades seria usar um dos compiladores de compiladores existentes para Java, porém isso daria muito trabalho.

O Rodrigo Kumpera sugeriu escrever o próprio parser, como fez o Gilad Bracha no Small Talk. Tanto o Rodrigo quanto o Renato Lucindo citaram o spirit++, que faz isso para C++. Uma pena não existir algo equivalente para o Java.

Phillip Calçado recomendou fazer um teste para saber se devemos ou não melhorar ainda mais essa DSL. O teste consiste em colocar a linguagem natural ao lado da DSL ruby e ver se o especialista consegue fazer a ponte entre uma e outra. Exemplo:

(1..50).collect{|x|
	cross(close, ema(x))
}

x de 1 a 50
       quando cruzar (fechamento, ema(x))

Se o especialista no domínio não entender a semelhança entre os dois códigos, é necessário aprimorar a DSL em questão.

Design Patterns no Java SE: o Template Method

Por Paulo Silveira em 04/09/07

Quando alguém aprende o que é Design Pattern, ou mesmo um novo Design Pattern, fica com aquele sentimento de que já viu isso em algum lugar antes. A API do Java SE é um excelente lugar para encontrar milhares de exemplos de Design Patterns. Só a java.util, a java.io e a java.lang já são suficientes para estudar os mais usados.

A classe abstrata java.io.InputStream é um excelente exemplo. Ela possui um conjunto de métodos para leitura de bytes, porém apenas um deles é abstrato: o método read que lê apenas um único byte. Segue seu fonte:

public abstract int read() throws IOException;

Ele é abstrato pois essa classe não sabe exatamente de onde será realizada a leitura: da entrada padrão? de um arquivo? de uma socket? Esse comportamento vai ser definido através da reescrita desse método em uma de suas subclasses concretas: FileInputStream, SocketInputStream, ByteArrayInputStream, entre outras. Essas sim sabem realizar a operação de leitura de um byte.

Se a classe InputStream não sabe ler um byte, como então é possível existir um método read que recebe um array de bytes a ser preenchido pela leitura, que não seja abstrato? Vamos ver o fonte deste método:

public int read(byte b[]) throws IOException {
   return read(b, 0, b.length);
}

Este por sua vez esta invocando o método sobrecarregado do read que recebe, além da array a ser preenchida, a posição inicial e quantos bytes devem ser lidos. O fonte deste método está abaixo:

01    public int read(byte b[]int off, int lenthrows IOException {
02       if (b == null) {
03          throw new NullPointerException();
04       else if (off < || len < || len > b.length - off) {
05          throw new IndexOutOfBoundsException();
06       else if (len == 0) {
07          return 0;
08       }
09 
10       int c = read();
11       if (c == -1) {
12          return -1;
13       }
14    
15       b[off(bytec;
16 
17       int i = 1;
18       try {
19          for (; i < len ; i++) {
20             c = read();
21             if (c == -1) {
22                break;
23             }
24             b[off + i(byte)c;
25          }
26       catch (IOException ee) {
27       }
28       return i;
29    }

Nas linhas 10 e 20 temos invocações ao método read que é abstrato! Isso é possível pois sabemos que não existe como instanciar a classe InputStream: ela é abstrata. Essa invocação recairá sobre um objeto que foi instanciado, logo ele possuirá uma implementação deste método read.

Esse é uma ótima ilustração do Template Method. Os métodos read que lêem mais de um byte são templates: eles possuem o algoritmo em si, mas ainda falta um pouco para que toda a funcionalidade deles esteja pronta. Essa parte que falta é suprida com a implementação concreta do método read nas classes filhas de InputStream. Quando a classe filha implementa esse método, os demais métodos de InputStream que dependem deste (os template methods) estarão prontos para uso!

Obviamente a OutputStream funciona de maneira análoga. No java.io ainda encontramos o Decorator Pattern, na java.awt o Composite Pattern, no java.lang temos o Builder e no java.util temos Iterator, Strategy, Prototype e muitos outros. Qual é seu exemplo preferido de Design Pattern dentro do Java SE?