Porque você deve trocar o Maven pelo Gradle?

Gradle

Qual ferramenta de build é a melhor?

Esse assunto é polêmico hein! Razão de inúmeros flamewars, brigas, mortes, guerras, protestos, atentados terroristas.

Não vou dizer que farei um análise imparcial aqui, porque não vou. Mas também não vou apenas ficar cuspindo no prato que comi – embora não sei porque isso é um problema, cuspir… em um prato vazio… qual problema… já comi mesmo…

Enfim! Minha ideia é apenas mostrar bons motivos para se usar o Gradle no lugar do Maven, assim como alguns anos atrás eu tive bons motivos para usar o Maven no lugar do Ant.

Muitos já se convenceram que o Gradle é uma alternativa mais interessante, inclusive o novo sistema de build do Android SDK é em Gradle.

Existe até uma teoria – bem justificada – do Neil Ford da Thoughtworks que eventualmente todo mundo vai ficar puto com o Maven e abandoná-lo.

Mas voltaremos nisso mais pra frente. Primeiro queria fazer um pequeno retrospecto autobiográfico…

Minha história com ferramentas de build

Meu background profissional é principalmente relacionado a Java e posso afirmar com bastante segurança que no mundo Java as ferramentas de build mais conhecidas e usadas são duas: o Ant e o Maven.

Tive a oportunidade de usar ambas na prática, em grandes e pequenos projetos, e essa foi minha experiência resumida com cada uma:

Experiências com Ant

– car***o, não tenho idéia do que esse build.xml de 32989 linhas faz…
– meu deus do céu, que target que eu rodo primeiro?

Experiências com Maven

– que maravilha, todos os 726362 projetos estão padronizados. É só fazer mvn install
\o/

– ah, agora eu sei que o source está em src/main/java e os resources em src/main/resources
\o/

– preciso implementar, buildar e distribuir um maldito plugin só pra fazer isso?!?!
<o>

– meu projeto precisa herdar desse mega-gigante pom.xml da empresa? Mas eu estou duplicando praticamente tudo novamente só para especializar meu build.
<o>

Explicando o que aconteceu:
Na época do Ant existia um grande problema de falta de padronização entre diferentes projetos. Cada um colocava o código fonte e os recursos onde julgava melhor, cada um colocava os artefatos gerados em uma pasta diferente, sem falar que muitos projetos não usavam nenhuma ferramenta para gerenciamento de dependências.

Além disso, os próprios arquivos descritores de build eram despadronizados. Se alguém trocava de projeto, a primeira coisa era descobrir qual target do Ant rodar primeiro.

Quando implantamos o Maven na empresa em que eu trabalho houve um ganho instantâneo em termos de padronização, adaptação de novas pessoas nas equipes e gerenciamento de dependências.

Claro que nem tudo é perfeito, no começo tivemos problemas com integração com IDEs, resistência e adaptação das pessoas, builds mais lentos. Mas os ganhos gerais compensaram essas dores de cabeça iniciais.

Mas depois de muitos anos usando o Maven, você começa a esbarrar em problemas muito inconvenientes a medida em que os builds se torna mais sofisticados e fora do comum, te obrigando a fazer vários malabarismos somente para contornar as imposições e limitações da ferramenta.

O Maven fez sua parte, mas já pode ir embora agora

Por ser uma ferramenta de opinião muito forte, a ponto de ser até considerada dogmática, ele conseguiu trazer uma padronização muito grande entre projetos da comunidade Java, criando praticamente uma nova cultura:

  • repositórios Maven
  • layout padrão de diretórios: src/main, src/test, target
  • build lifecycle

Mas se o Maven trouxe tantos benefícios, porque alguns estão trocando ele pelo Gradle?

Gosto de pensar no Maven como uma grande ditadura totalitária que veio instaurar a ordem quando tudo estava em um estado de total caos. Você tem que seguir o modelo rígido de build que ele impõe, seguir somente as fases que ele determina, ou então você terá muito trabalho subvertendo o sistema!

Imposition over Configuration

Assim o Maven funciona muito bem para 90% das coisas mais comuns de um build, mas complica muito para aqueles 10% de detalhes específicos do seu projeto (leia mais sobre a Dietzler’s Law no link do Neal Ford acima), já que a maneira como o Maven permite extensão é limitada e complicada demais.

Já está na hora dessa ditadura acabar e dar origem a um sistema mais flexível.

Gradle junta o melhor dos dois mundos e vai além

Enquanto o Ant oferece total flexibilidade para você definir as tarefas do seu build e como elas se sucedem umas as outras, o Maven vem com a proposta de um ciclo de vida rígido que todo build deve seguir.

A proposta do Gradle é continuar reforçando essa cultura e padronização do Maven através de convenção ao invés de imposição.

O Gradle incorporou o melhor dos dois lados:
Do Ant:

  • tasks altamente configuráveis
  • ciclo de vida de build flexível baseado em grafo aciclico direcionado.
  • gerenciamento de dependências com repositórios Ivy

Do Maven:

  • padronização do layout de diretórios
  • padronização do build lifecycle
  • builds multi-projeto
  • gerenciamento de dependências com repositórios Maven

Mas o Gradle vai muito além do Ant e do Maven: ele implementa uma verdadeira DSL em Groovy, permitindo a maior flexibilidade possível; no fim das contas, seu descritor de build é um script!

Enquanto no Maven e no Ant para criar uma coisinha diferente para seu build é preciso que você implemente e disponibilize um plugin, no Gradle você pode programar sua task ali mesmo, no build file.

Perai, o descritor de build é um script, então significa que vou voltar para o caos novamente?

Não, não vai, e o segredo está em seu sistema de plugins.

Conhecendo melhor o Gradle

No Gradle, a unidade de trabalho é a Task; equivalente ao Target do Ant ou ao Phase e o Goal do Maven.

Como no Ant, essas tasks podem ter dependência para outras tasks, criando um grafo direcionado acíclico. Dessa forma, quando você executa uma task, o Gradle executa antes todas as dependências dessa task.

No entanto, o Gradle conta com um sistema de plugin, e vários plugins nativos, que evitam que os builds voltem para o caos novamente, através da convenção.

Um plugin no Gradle é essencialmente um conjunto de tasks e configurações pré-definidas, que você pode importar em seu projeto. É como se um plugin no Gradle fosse uma Trait do Scala ou um Mixin do Ruby, ficando muito fácil estender um build através da composição e não da herança.

Portanto com o Gradle temos tanto a convenção, que nos permite ter a padronização e o inicio rápido de um novo projeto, como também a flexibilidade para customizar o build da maneira que for necessária, através de uma poderosa DSL Groovy.

Veja como é simples um build file para um projeto Java, com classes, resources e testes unitários.

Arquivo build.gradle:

apply plugin: 'java'

Sim, com uma linha você tem um projeto Java que compila, testa e empacota um JAR.

Outras vantagens do Gradle

Só uma listinha para te convencer a usar o Gradle:

  • Descritor de build em Groovy e não em XML
  • Suporte a repositórios Maven e Ivy
  • Build incremental que funciona de verdade
  • Composição ao invés de Herança
  • Suporte nativo a várias linguagens como Groovy e Scala
  • Integração com Eclipse, IntelliJ
  • Dezenas de plugins disponíveis
  • Android SDK e Hibernate já usam (vai ficar fora dessa?)

Estou convencido! E agora?

Shut up and take my money
Eu pretendo fazer outros posts sobre o funcionamento do Gradle, tentando destilar um pouco da documentação que tem por aí, mas para quem ficou entusiasmado com o Gradle, o guia dele é bem completo.

Também recomendo o livro Building and Testing with Gradle que é gratuito para leitura online.

O que é design de software?

Nós desenvolvedores, artesãos de software ou simplesmente programadores, vivemos em uma jornada incessante em busca do aprimoramento em nosso ofício.

Afora o estudo de novas tecnologias, novos processos e melhores algoritmos para resolvermos os problemas de forma mais eficiente, uma das habilidades mais importantes a ser buscada por um programador é a de se fazer um bom design.

A existência de um bom design é independente de tecnologia ou problema, e é a razão por trás de todos os princípios de programação que se encontra por aí.

Mas o que é design?

Por ser uma palavra da língua inglesa vejamos primeiro sua definição nessa língua:


Merriam-webster:
- the arrangement of elements or details in a product or work of art
- deliberate purposive planning
- a preliminary sketch or outline showing the main features of something to be executed

Em português, design é geralmente traduzido como:


- projeto, plano, desenho

Vemos que o termo design está intimamente associado com o planejamento de uma atividade que, em geral, dará origem a algum artefato como resultado. Uma atividade de construção, digamos assim.

Dessa forma, antes de iniciar a execução da construção do artefato, realiza-se um plano que servirá como um guia nessa tarefa, delineando a forma, organização e detalhes dos elementos que compõem tal objeto.

Mas design é só isso?

De fato podemos ir além da noção de planejamento e pensar em design como algo mais concreto: desenho, forma, disposição de elementos, organização – como usualmente usa-se o termo nas artes plásticas.

Quando você ouve uma música, contempla uma pintura ou lê um poema, você pode perceber claramente sua forma: a escolha, disposição e interação entre elementos que compõem aquela obra e a caracterizam.

Então, após a execução de uma obra – e aqui englobamos qualquer ofício – pode-se dizer que o design fica concretizado, impresso no artefato criado, tornando-se parte dele, parte de sua história.

Portanto, podemos afirmar que design é a organização de elementos que ocorre tanto antes, durante ou depois da existência do artefato, e é um processo inevitável.

Por que se pensa em design?

Se o processo de design é inevitável, então posso simplesmente esquecê-lo, executar e parar de me preocupar com isso?

Claro que não. O design deve ser sempre cuidadosamente pensado, consciente e acima de tudo alinhado com o papel que ele desempenha na atividade. Caso isso não aconteça, o resultado será um artefato com um design ruim, onde bom e ruim são conceitos relativos ao contexto em que ele existe.

Precisamos ter em mente que para cada tipo de atividade o design tem um propósito diferente e é com base nesse propósito que classificamos um design como bom ou ruim. Alguns exemplos:

Na arte o objetivo do design é a estética.
Na construção civil o objetivo do design é dimensionar estruturas, mitigar riscos, reduzir custos.
No front-end web, o objetivo é a estética e a usabilidade.

O propósito do design no software

No desenvolvimento de software o objetivo do design é facilitar a mudança.

Danilo Sato, da Thoughtworks, em uma palestra explica que para ele Design é Código, e ele está certíssimo, porque o código é o artefato do programador, e o design está ali, concretizado no código. Cada decisão tomada antes e durante seu desenvolvimento será refletida no código.

Mas indo um pouco além, o design também é a atividade que ocorreu antes da existência do código, é também o diagrama de classes desenhado no quadro branco; design também é planejamento.

Beethoven fazendo Big-Upfront-Design

Beethoven fazendo um Big Design Upfront

Como na analogia com a música feita por Rich Hickey em uma palestra inspiradora, mais decisões de design podem ser tomadas antes da execução, em diagramas UML por exemplo, analogamente a um compositor que escreve uma sinfonia; ou mais decisões de design podem ser adiadas, basicamente deixando o programador livre para tomá-las durante a execução, como um jazzista improvisando sobre uma sequência de acordes apenas.

No mundo do software, como o objetivo do design é facilitar a mudança, e o rítmo das mudanças é alucinante, uma tendência é jogar o máximo das decisões para frente, até o ponto que chegamos no Design Emergente. Dessa forma, só inserimos complexidade no design do nosso sistema quando essa complexidade for de fato necessária, seguindo o bom e velho princípio do YAGNI.

Estudar Design de Software é essencial

A diferença crucial entre um grande design planejado – como uma composição orquestral, e um design ágil – como uma improvisação de Jazz, é que para esse último caso é obrigatório que o executor, seja ele o músico ou o programador, tenha um grande conhecimento sobre como fazer um bom design.

No caso ágil, as decisões de design são tomadas na hora, pelo executor, baseado no contexto daquele exato momento, com as informações mais atuais possíveis e com o feedback imediato, por isso é preciso que o executor tenha interiorizado os princípios que regem um bom design.

O “artefato software” não é tão efêmero quanto uma improvisação de uma música, que só acontece uma vez, nem tão imutável quanto uma estátua de mármore, ele vive em algum lugar no meio desse espectro. Por ter essa natureza mais “flexível”, temos a possibilidade de remodelá-lo constantemente com mais facilidade, criando o design mais apropriado durante a construção, e adaptando-o a cada iteração, através da refatoração.

Portanto, como um artesão de software, interiorize a “harmonia”, pratique as “escalas”, estude os melhores “solos” e, acima de tudo, nunca se esqueça do propósito do design que você está criando.

Miles Davis fazendo design emergente

Miles Davis fazendo design emergente

Performance em queries SQL usando IS NULL

Hoje, depois de o que? Uns 2 anos de hiato? Volto com uma crônica.

Prólogo

Estava hoje eu analisando a performance de uma aplicação Java no trabalho e me deparei com algo que me surpreendeu.

O Profiler do VisualVM estava me mostrando um método que estava demorando demais. Esse método era muito simples, sendo que apenas executava uma consulta no banco de dados (relacional), algo parecido com:

SELECT * FROM Produto p WHERE p.categoriaId IS NULL

Digo parecido porque na realidade era uma query em HQL sobre outra entidade, mas a ideia é a mesma

Muito intrigado abri meu Squirrel  (cliente SQL), joguei a query lá para olhar o seu plano de execução.

Ato 1 – Full Table Scan

Para minha surpresa o banco estava fazendo um full table scan na tabela de Produtos (que é enorme). Ou seja, ele estava passando por cada um dos registros olhando se a tal categoriaId era nula.

Logo pensei:

- Ah! Problema resolvido! Falta um índice na categoriaId.

Não, não faltava… Para minha surpresa o banco não estava indexando o NULL…

Ato 2 – Indexando o Null

Nem o Oracle, nem o Postgres (< 8.3) indexam NULL, apenas valores de verdade (não sei se outras marcas de BD indexam, consulte o manual). E agora? O que fazer?

Uma forma de resolver essa questão é justamente transformar o NULL em um valor de verdade, digamos 0 ou “Null”, dependendo do tipo da coluna que você vai indexar, de modo que ele faça parte do índice.

Para fazer isso podemos usar a função COALESCE(), que existe nos bancos relacionais mais conhecidos.

A função COALESCE(value, …) recebe um ou mais parâmetros e devolve o primeiro parâmetro não nulo, se houver.

Assim, ao invés de criar um índice para a coluna, vamos criar um índice para uma função sobre aquela coluna.

CREATE INDEX in_produto_categoriaid ON produto( COALESCE(categoriaid, 0) )

Aqui usamos COALESCE(categoriaid, 0) porque nossa categoriaid é uma coluna numérica. Se fosse uma coluna de tipo texto poderíamos usar COALESCE(coluna_texto, ‘Nulo’). O importante é usar um valor default que com certeza nunca será inserido nessa coluna.

Ato 3 – Adaptando as sua Queries

A desvantagem dessa solução é que precisamos mudar nossa consulta para fazer restrições sobre a função da coluna ao invés da coluna em si.

No nosso exemplo teríamos que mudar a consulta original de:

SELECT * FROM Produto p WHERE p.categoriaId IS NULL

Para:

SELECT * FROM Produto p WHERE COALESCE(p.categoriaId, 0) = 0

Assim, com certeza o banco usará o índice e você evitará um full table scan!

Epílogo

Cuidado com consultas que restringem apenas por IS NULL ou IS NOT NULL, especialmente em tabelas com muitos registros. Em alguns bancos de dados, os valores nulos não são indexados, e a sua consulta vai ficar pesadíssima.

A solução apresentada aqui é bem satisfatória, mas tem o porém de precisar de uma alteração na query em si, o que pode ser problemático, dependendo da sua aplicação.

Por fim, algumas observações:

  • Se você usa Hibernate ele tem suporte para a função COALESCE, o que é bom
  • O Oracle 11g supostamente tem uma opção para indexar valores nulos. Criar o índice dessa forma não surtiu efeito nos meus testes de fato funciona!
  • Parece que existe um comportamente estranho (bug?) no Hibernate + HSQLDB + COALESCE. Então, se você usa HSQLDB fique esperto com isso.

Fin

Errata

Inicialmente eu havia dito acima que criar o índice da forma mostrada no link acima, usando uma constante para fazer um índice composto, não funcionava:

CREATE INDEX in_produto_categoriaid ON produto( categoriaid, 1) )

Mas na verdade eu estava enganado nos meus testes. Esse workaround funciona e faz com que as colunas com NULL sejam de fato indexadas.

Vou explicar onde errei:

Primeiramente eu analisei o plano de execução da busca com IS NULL e vi que o Oracle fazia um TABLE ACCESS FULL. Logo concluí, precipitadamente, que o índice não estava sendo usado por que o NULL não estava indexado.

Mas acontece que havia muitos registros com categoriaid null, e se os predicados de restrição usados pela sua busca restringir uma quantidade de registros maior que cerca de 20% do total de registros, o banco em geral vai dar preferência por fazer um full table scan por ser menos custoso que encontrar todos esses 20% de registros a partir do índice.

Isso porque o banco é otimizado para fazer esses tipos de acesso sequencial aos dados.

Type-safe null usando Option

Reconhecendo o erro bilionário

Em 1965, Sir Charles Antony Richard Hoare (Tony Hoare para os chegados), inventava o null, as referências nulas, enquanto projetava a linguagem orientada a objetos ALGOL W.

Muitos anos depois, em 2009 ele mesmo retoma esse fato em uma palestra:

I call it my billion-dollar mistake.

Ele chama o null de seu erro bilionário! Fonte de inúmero erros, vulnerabilidades e problemas em sistemas no mundo todo. De fato uma grande cagada!

O Problema e sua Consequências

Certamente o null é tratado com muita cautela por nós programadores quando escrevemos nosso código. Sendo profissionais cautelosos, sempre pensamos que uma ou outra referência pode estar nula, desconfiados de toda e qualquer variável.

O programador Java incauto, faria coisas do tipo:

Animal elefante = animais.get("elefante"); 
elefante.anda(); // Pode dar merda
Pessoa p = PessoaDAO.findByName("Felipe");
Documento d = p.getDocumento(); // Vai dar merda
d.getNumero(); // Já deu merda

Já o programador cauteloso e experiente não se arriscaria a tomar um NullPointerException na cara:

Animal elefante = animais.get("elefante");
if(elefante != null)
  elefante.anda();  // Agora é seguro
Pessoa p = PessoaDAO.findByName("Felipe");
if(p != null) {
  Documento d = p.getDocumento();
  if(d != null) { 
    d.getNumero();  
  }
}

Ser desconfiado dá trabalho, e deixa nosso código cheio de if-guards, que são feios e chatos de ler.

Claro que, com um bom design, é possível diminuir a quantidade de ifs, mas o grande problema é que eventualmente alguém vai esquecer do maldito if e um NullPointerException (ou Segmentation Fault) poderá explodir a qualquer momento em produção!

O problema é que o compilador não nos diz nada. Se fosse possível fazer o compilador trabalhar para gente, e pegar todos esses erros em compilação seria ótimo…

Evitando o erro

Muitas pessoas inteligentes perceberam o risco de se ficar manipulando nulls e inventaram maneiras para evitar seu uso.

Null Objects

Um exemplo é o pattern Null Object, que sugere a construção de objetos “sem comportamento” como uma alternativa.

O grande problema desses Null Objects é que se forem usado sem cuidado, podem introduzir bugs difíceis de se encontrar, pois eles de certa forma disfarçam o problema. Imagine: você tem um objeto na mão, chama alguns métodos sem nenhum problema só que na verdade esses métodos não estão fazendo nada.

Option

Um conceito muito interessante e bastante simples, trazido diretamente do mundo da programação funcional é o do tipo Option, também chamado de Maybe em algumas linguagens.

O Option é um tipo abstrato parametrizado (portanto Option[T]) que tem apenas dois filhos: Some[T] e None. De maneira muito simplificada, em Java seria algo do tipo:

public abstract class Option<T> {
	  public static <T> Option<T> of(final T conteudo) {
		if(conteudo == null) {
			return new None<T>();
		} else {
			return new Some<T>() {
		    	public T get() {
		    		return conteudo;
		    	}
		    };	
		}
	  }
}

abstract class Some<T> extends Option<T> {
  public abstract T get();
}

class None<T> extends Option<T> {}

Usamos o Option assim:

Option<String> some = Option.of("Tem coisa"); // devolve Some<String>
Option<String> none = Option.of(null); // devolve None

Portanto, sempre que uma função tem a possibilidade de devolver um valor inválido ou inexistente para determinado argumento, podemos embrulhar o resultado em um Option.

Por exemplo:

Option<Pessoa> pessoa = pessoaDAO.findById(1234L); 

Legal, dessa maneira pessoa nunca será null, mesmo que o registro 1234 não exista no banco de dados.

Só que quando eu quiser o conteúdo do Option, teria que fazer isso:

if(pessoa instanceof Some<?>) { 
  String nome = ((Some<Pessoa>) pessoa).get().getNome(); 
  System.out.println("Encontrei pessoa com nome: " +  nome); 
} else {
  System.out.println("Não encontrei ninguém");
}

Esse código, apesar de terrivelmente feio, é mais type-safe, já que o compilador nos força a fazer uma verificação antes de usar a instância de Pessoa.

Outra desvantagem dessa abordagem é que, além de ser desengonçado, é mais prático fazer um if(pessoa != null) . Por isso é difícil convencer um programador a usar isso. E agora?

Bom, em Scala e em outras linguagens mais funcionais temos Pattern Matching para melhorar as coisas:

Option<Pessoa> pessoa = pessoaDAO.findByPK(1234L); 
pessoa match {
  case Some(p) => println("Encontrei pessoa com nome: " + p.nome)
  case None => println("Não encontrei ninguém")
}

Ainda um pouco verboso, mas bem melhor!

Pattern Matching é ideal quando temos dois fluxos completamente diferentes dependendo se o Option é Some ou None. Para casos em que só se quer passar o valor para uma função, ou manipulá-lo de maneiras mais simples, podemos usar as construções apresentadas na próxima seção.

Indo um pouco além do óbvio

Option surgiu no mundo funcional, e é muito mais do que um simples container de coisas.

Na realidade, é possível manipular um Option de maneiras muito práticas e poderosas, especialmente em linguagens em que conseguimos passar funções como parâmetro. Essas construções geralmente são mais enxutas do que fazer Pattern Matching.

Tony Morris fez um post em seu blog mostrando algumas formas mais funcionais de se substituir diversos casos implementados com Pattern Matching, usando a própria API do Option.

Veja alguns exemplos em Scala:

Option<Pessoa> pessoa = pessoaDAO.findByPK(1234L);  // mesmo exemplo

// foreach
// Se for Some executa a função passada, se for None não faz nada.
pessoa.foreach(p => println(p.nome))

// map
// Se for Some executa a função e embrulha o retorno em um Option. Se for None devolve None.
val endereco:Option[Endereco] = pessoa.map(_.endereco) 

// getOrElse
// Se for Some devolve o conteúdo, se for None devolve o resultado do bloco passado como parâmetro
val p = pessoa.getOrElse(new Pessoa())

Note que muitos métodos de Option são os mesmos que existem nas Collection em Scala.
E pasmem, é possível usar Option em for-comprehensions, podendo até misturar listas com options.

val lista:List[Integer] = List(1,2,3)
val option:Option[Integer] = Some(1)

// Conhecemos for em listas
for( num <- lista) {
  println(num)
}

// Mas é possível usar Option
for(num <- option) {
  println(num)
}

Imagine que você quer navegar por uma estrutura de objetos cujos métodos retornam Option. É possível usar uma construção for sofisticada em Scala:

for { 
  pessoa <- pessoaDAO.findByPK(1234L)  
  endereco <- pessoa.getEndereco
  numero <- endereco.getNumero 
} {
  println("O número da casa da pessoa de ID 1234 é " + numero)
}

Se em alguma dessas linhas, o objeto for um None, o corpo do for não será executado. É como se um Option fosse uma lista de um único elemento.

Por fim…

Option é uma poderosa abstração para substituir o famigerado null, causa de tantos NullPointerExceptions inesperados.

Seu objetivo é tornar o código mais type-safe e, mesmo adicionando uma camada extra para alcançar isso, sua API engenhosa permite que o programador manipule os Option de maneiras muito eficientes, concisas e poderosas.

Quando estiver projetando uma API, considere o uso de Option em funções que podem ou não devolver um objeto. Use para tratar os casos de exceção, como por exemplo quando um registro não é encontrado no banco de dados, ou quando uma função não está definida para determinado argumento.

Design Patterns em Scala – Parte 3: Pimp My Library

Nas duas primeiras partes da série Design Patterns em Scala, vimos alguns patterns bem conhecidos, porém implementados de uma maneira um pouco diferente em Scala.

Agora veremos um pattern que não é conhecido no mainstream Java, porque usa alguns recursos exclusivos da linguagem Scala, no caso de hoje esse recurso é a conversão implícita.

Estamos falando do Pimp My Library! Yooo!

Motivação

Se você leu o post sobre conversões implícitas, deve se lembrar do exemplo abaixo:

scala> val str = "Hello"
str: java.lang.String = Hello

scala> str.toList
res0: List[Char] = List(H, e, l, l, o)

E agora, com toda a mágica desmistificada, você deve saber que está ocorrendo uma conversão implícita de String para alguma classe que contenha o método .toList().

Mas sem pensar no que está acontecendo por baixo dos panos, qual a sensação que temos quando escrevemos o código acima? Qual a percepção do desenvolvedor?

Parece que foram acrescentados métodos diretamente na classe final String, não é? Se observarmos o código, não dá nem para saber qual a classe que efetivamente define o método .toList().

Esse exemplo é exatamente uma aplicação do pattern Pimp My Library! A classe String foi… éé… “Pimpada”!

Ok, “incrementada”, “aumentada”, “melhorada”, são termos melhores… Mas enfim…

O Pimp My Library é útil para os casos em que você usa uma classe ou API que não é sua e deseja estendê-la, de maneira transparente, para melhor se adaptar às suas necessidades.

Esse pattern foi definido (e nomeado) pelo próprio Martin Odersky, criador da linguagem, nesse artigo.

A primeira vista esse pattern se parece muito com Ruby Open Classes ou até mesmo Prototypes em Javascript, no sentido de que se consegue atingir o mesmo efeito.

No entanto, fazer Pimp My Library em Scala é algo muito mais controlado e localizado. Você não está efetivamente fazendo uma alteração na classe e afetando todo restante da sua aplicação (o que pode sair do controle e introduzir bugs estranhos).

É mais controlado fazer isso em Scala porque você precisa trazer explicitamente para o escopo do seu código o método implicit que faz as coisas acontecerem.

Vamos entender melhor como funciona…

Implementação

Agora que sabemos como funcionam as conversões implícitas fica fácil fazer o resto!

Tudo que precisamos é fazer um rich wrapper que possui os métodos que desejamos acrescentar a classe a ser incrementada.

Você adoraria que String tivesse um método inverte() que te devolve a String de trás para frente? E mais, você quer esse método em português?? Sem problemas, faça você mesmo:

// A primeira coisa que você precisa é de um wrapper bem legal
class StringBombada(str:String) { 
  def inverte():String = {
    val listaRev = str.toCharArray.foldLeft(new ListBuffer[Char]) { (lista, char) => char +: lista }
    listaRev.mkString  
  }
}

// A segunda coisa é uma conversão implicita no escopo. 
// Você pode deixar esse método dentro de um object.
implicit def stringParaStringBombada(str:String):StringBombada = {
  new StringBombada(str)
}

E a prova final:

scala> "socorram-me subi no onibus em marrocos".inverte
res17: String = socorram me subino on ibus em-marrocos

E é só isso. Lembrando que a conversão só acontecerá quando o implicit estiver no escopo.

Quick Pimp

Se você quer fazer uma coisa mais rápida, adicionar só um método aqui e ali, e acha que escrever um Wrapper é muito trabalhoso, apresento-lhe o Quick Pimp.

O Quick Pimp é uma versão light do Pimp My Library, que usa Structured Typing e Classes Anônimas ao invés de um Wrapper completo e dedicado:

implicit def string2paraInteiro(s: String): { def paraInteiro: Int } = new {
  def paraInteiro: Int = java.lang.Integer.parseInt(s)
 }
}

Veja, só com esse implicit no escopo eu consigo fazer isso:

scala> "12345" paraInteiro
res1:Int = 12345

Prático, conciso, elegante…

O QuickPimp se baseia em Structural Typing que internamente depende de reflection. Portanto, pode ter algum impacto em partes em que a performance é crítica.

Recapitulando

Nesse post começamos a entrar mais a fundo no mundo exclusivo de Scala, conhecendo um pattern muito usado, inclusive na própria API do Scala, chamado Pimp My Library, e sua variação light Quick Pimp.

Esse pattern lembra os open-classes e monkey patching do Ruby, mas de uma forma mais controlada e type-safe. É extremamente útil para incrementar classes e APIs existentes, mesmo que elas sejam de terceiros, e mesmo que elas sejam final.

E por hoje é só!

Implicits em Scala – Conversões implícitas e dinamites

Explícito é o que você está acostumado a ver na TV de madrugada, implícito é outra coisa. Veja:

implícito
adj.
1. Incluído, contido (ainda que não expressado), subentendido.

Isso mesmo! Conversões implícitas são conversões subentendidas, que acontecem por baixo dos panos, que o compilador faz sem você perceber.

E para que serve isso? É esse o segredo da mágica do Scala! É o pó mágico que nos permite fazer DSLs que são ao mesmo tempo poderosíssimas e type-safe!

Já se imaginou fazendo isso:

scala> val str = "Hello"
str: java.lang.String = Hello

scala> str.toList
res0: List[Char] = List(H, e, l, l, o)

Eu olhei a API de java.lang.String e não tem nenhum método toList! E String é final!

E que tal definir mapas assim?

scala> val mapa = Map(1 -> "Um", 2 -> "Dois")
mapa: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,um), (2,dois))

Não, nada disso é nativo da linguagem. São apenas conversões implícitas de String para alguma classe que contenha o método toList, e de Int para alguma classe que contenha o método ->.

Implicit Methods

A mágica aí em cima é feita por métodos implícitos que estão definidos no objeto scala.Predef que é importado automaticamente pelo compilador.

Mas como esses métodos implícitos funcionam?

O conceito é bastante simples na verdade. Um método implícito é um método que recebe um objeto de alguma classe e devolve um objeto de outra classe, e também é explicitamente marcado como implícito =) .

Vamos fazer alguns exemplos. Primeiro vamos definir o seguinte método:

def imprime(str:String) = println(str)

Um método bobo que recebe uma String e a imprime no console. Vai lá, faça um teste no console do Scala:

scala> imprime("Uma String")
Uma String

Agora tente passar um Int para nosso método:

scala> imprime(1234)
found : Int(1234)
required: String

Holy Shit! Erro de compilação! 1234 não é uma String.

Agora defina o seguinte método implícito de conversão de Int para String.

// Não faça isso de verdade, ok?
implicit def intToString(inteiro:Int):String = {
  println("Implicit em ação!") // Isso é para provar que essa função é chamada
  inteiro.toString
}

Agora vamos tentar chamar nosso método mais uma vez com um inteiro:

imprime(1234)
Implicit em ação!
1234

Aha! Como esperado! Nosso implicit foi chamado e o parâmetro foi convertido.

O compilador muito inteligentemente fez o seguinte:

  • viu que o método imprime recebe uma String como parâmetro, mas o programador passou um Int no lugar.
  • então ele procurou no escopo da chamada, algum método marcado com implicit que recebe um Int e devolve uma String. No nosso caso o método intToString.
  • colocou uma chamada para intToString(1234) substituindo o parâmetro 1234.

Resumindo, ele transformou isso:
imprime(1234)
Nisso:
imprime(intToString(1234))

Se você não estivesse tão maravilhado com esse fantástico exemplo, talvez estivesse pensando:
- Essa ***** não explica os exemplos do começo do Post!

Muito perspicaz você hein!

O fato é que além de funcionar no momento de passagem de parâmetros, as conversões implícitas também entram em ação em chamadas de método, ou seja, se você tentar chamar um método que não existe em uma classe XYZ, o compilador vai procurar por um método implícito no escopo que saiba converter a classe XYZ para alguma classe que contenha o método chamado.

Por exemplo:

class DateHelper(val qtd:Int) {
  def segundos = qtd * 1000L
  def minutos = 60 * segundos
  def horas = 60 * minutos
  def dias = 24 * horas
}

Esse wrapper é construido passando-se um inteiro para ele. Quando chamamos qualquer um de seus métodos, ele devolve uma quantidade de milisegundos se for segundos, minutos, horas ou dias.

Normalmente isso não seria algo muito prático de se usar:

val dh = new DateHelper(2);
dh.minutos // 120000

Mas se tivermos um implicit de Int para DateHelper, as coisas ficam muito mais interessantes:

implicit def intToDateHelper(qtd:Int) = new DateHelper(qtd)

E portanto teremos algo muito fantástico:

scala>2 minutos
120000
scala>5 dias
432000000

Praticamente estamos escrevendo em português aqui =)

E é assim que podemos fazer poderosas DSLs em Scala!

Implicit Parameters

Em Scala existe um outro tipo de implict. Os parâmetros implícitos.

Quando um parâmetro marcado como implicit não é fornecido pelo programador, o compilador vai procurar no escopo da chamada alguma variável também marcada como implicit que possa ser usada como parâmetro.

Imagine a função:

implicit val numero = 10

def soma(a:Int)(implicit b:Int) = a + b

soma(2)(2) == 4  // Se não precisar, o implicit não é usado
soma(2) == 12 // usa implicit
soma(1) == 11 // usa implicit

Uma das aplicações disso é o uso de Manifests para obter informações de tipo em runtime. (um dia escrevo um post sobre isso).

Algumas regras

O compilador segue algumas regrinhas para determinar qual implicit usar e quando usar.

Basicamente:

  • Somente é considerado o que estiver marcado como implicit
  • Somente é considerado os implicits que estiverem no escopo.
  • Se o compilador encontrar mais de um implicit que possa ser aplicado (ambiguidade), dá erro.
  • Somente UMA conversão implícita é tentada pelo compilador. Por exemplo, se um método espera C, você passa A, e existe uma conversão A->B e outra B->C. O compilador NÃO faz.
  • O compilador só faz uma conversão se for necessário. Se o que estiver escrito fizer sentido (type-check), o compilador não aplica nenhuma conversão.

E as dinamites?

O que diabos dinamites tem a ver com isso?

Quem já assistiu a série Lost deve se lembrar do finado Dr. Artz que morreu explodido por uma dinamite.

O que eu quero dizer aqui é simples: Implicits são extremamente poderosos, mas tome muito cuidado ao usá-los. Não saia por aí fazendo conversões implícitas sem cautela porque você vai:

  • Criar um código muito difícil de entender.
  • Introduzir os bugs mais bizarros que você já viu no seu código.
  • Morrer de caganeira.

Com grande poder vem grande responsabilidade.

Pense nisso. (ou se exploda, você quem sabe…)

Design Patterns em Scala – Parte 2: Decorator

No post anterior dessa série de Design Patterns falamos um pouco sobre maneiras diferentes de se implementar o pattern Observer em Scala.

Agora vejamos como as Traits podem nos ajudar a “decorar” nossas classes em runtime, com o Pattern Decorator.

Decorator

Muitas vezes quando queremos adicionar um comportamento a alguma classe, pensamos logo em herança, só que herança nem sempre é o melhor caminho. Vejamos o famoso exemplo das Janelas (decoração, janelas… acho vou mudar de ramo.).

Suponha uma classe que desenha uma janela simples na tela.

class Janela {
  def renderiza() = {
    print("Desenhando janela") // Use a imaginação cara!
  }
}

Ótimo, só que em um sistema de janelas, podemos ter diversos tipos de janelas, cada uma com uma combinação de funcionalidades:

  • Janela com barra de título
  • Janela com scroll vertical e barra de título
  • Janela com scroll horizontal, barra de título e barra de status
  • etc etc etc

Já de cara podemos ver que é impraticável criar uma subclasse para cada tipo de janela.

Aí vem a sacada do Decorator: ao invés de herança vamos usar composição e compor, a partir de pedaços de funcionalidade, a janela que quisermos. Tudo em runtime.

Vamos ver como implementar em Java da maneira chata usual:

// Interface implementada pela classe e pelos seus decorators
public interface Janela {
  public void renderiza();
}

// Decorator tem que manter controle de quem ele está decorando
abstract class JanelaDecorator implements Janela {
  private Janela janela;

  public Janela getJanela() {
    return this.janela;
  }

  public JanelaDecorator(Janela janela) {
    this.janela = janela;
  }

  public abstract void renderiza();
}

class JanelaSimples implements Janela {
  public void renderiza() {
    System.out.print("Desenhando Janela");
  }
}

class ScrollBarDecorator extends JanelaDecorator {
  public ScrollBarDecorator(Janela janela) {
    super(janela);
  }

  public void renderiza() {
    this.getJanela().renderiza();
    System.out.print(" com ScrollBar");  // Decorator adiciona comportamento
  }
}

Então, quando você tiver um monte de decorators implementados, você pode começar a decorar suas janelinhas:

Janela j = new ScrollBarDecorator(new JanelaSimples());
j.renderiza();
// Desenhando Janela com Scrollbar

// Você pode decorar com quantos decorators quiser.
Janela j = new ScrollBarDecorator(new TitleBarDecorator(new StatusBarDecorator(new JanelaSimples())));

Muito legal, mas eu só mostrei isso para você poder comparar com Scala.

Em Scala podemos fazer isso usando Traits:

// Nossa Janela
class Janela {
  def renderiza = print("Desenhando Janela")
}

// Quando uma Trait estende uma classe, ela só poderá ser misturada a objetos daquela classe. E também, "super" irá se referir à classe a qual ela foi misturada, ou outra Trait que foi misturada na classe.
trait ScrollBar extends Janela {
  override def renderiza = {
    super.renderiza
    print(" com Scrollbar")
  }
}

// Vamos usar
val janela = new Janela with ScrollBar
janela.renderiza 

// Desenhando janela com Scrollbar

Só isso! Meus dedos agradecem a redução de linhas.

E também aqui você pode usar quantos decorators quiser, separando as Traits com a palavra-chave with:

// "super" sempre vai se referir ao elemento da esquerda. 
val janela = new Janela with ScrollBar with StatusBar with TitleBar with Resize with BolinhasVerdes

Nesse exemplo, quando invocamos o método renderiza() no objeto janela, o primeiro
método a ser chamado é o da Trait a extrema direita BolinhasVerdes. A medida em que invocamos o método em
super, estamos referenciando a Trait logo a esquerda, até chegar na classe Janela.

É assim que funciona a composição de Traits.

Por hoje é só pessoal, e no próximo capítulo sobre Design Pattern:

Pimp My Library

Design Patterns em Scala – Parte 1: The Observer

Todo programador moderno, hype, antenado, ligado nas tendências, curte mesmo um Design Pattern.

Aliás tudo que tem Design no nome fica chique né? “Design de Sombrancelhas”, “Hair Designer”. Mas enfim…

Design Patterns é de fato um assunto bastante interessante, e o conhecimento de alguns desses padrões de design pode facilitar bastante o desenho de soluções.

Nesse post não pretendo ficar explicando a fundo os Patterns mais conhecidos no mundo Java, tem muita coisa já escrita por aí.

Como o foco principal desse blog é a linguagem Scala, vamos dar uma olhada em como alguns Patterns manjados em Java ficam ainda mais interessantes em Scala. Em seguida, vamos ver alguns Patterns novos para Scala.

Singleton

object MySingleton {
 // Código vai aqui
}

Singleton foi elevado ao cargo de construção de linguagem! Usando a palavra-chave “object” estamos criando uma instância única de uma classe.

No exemplo acima estamos ao mesmo tempo declarando uma classe e instanciando um singleton para ela.

Um singleton pode naturalmente estender classes e implementar Traits.

// Nem preciso de corpo se eu não quiser
object MySingleton extends MyClass with MyTrait 

O ministério da saúde dos programadores adverte: Singleton faz mal aos testes. Use com consciência.

Observer

Como vocês já sabem, nesse padrão temos dois personagens principais: o Observer, e o Subject.

O Observer é o objeto que está interessado no que o Subject está fazendo, e o Subject é gentil em notificar os Observers quando algo nele muda.

Primeiro, vejamos o jeito mais conhecido, java-like:

trait Subject {
  private var observers: List[Observer] = Nil

  def addObserver(observer: Observer) = observers = observer :: observers

  def notifyObservers() = observers.foreach(_.receiveUpdate(this))
}

trait Observer {
  def receiveUpdate(s:Subject)
}

// Olha mãe, sou um Singleton
object MyObserver extends Observer {
  def receiveUpdate(s:Subject) {
    println("O subject mudou!")
  }
}

// Olha mãe, eu nem preciso de um corpo
object MySubject extends Subject

// Testa tudo isso aí
MySubject.addObserver(MyObserver)
MySubject.notifyObservers

Criamos uma Trait para o Subject e o Observer e um exemplar concreto para cada um apenas para ver funcionando.

Uma Trait é como se fosse uma interface Java que também pode ter campos concretos. Uma classe consegue “estender” múltiplas Traits.

Só que em Scala eu consigo deixar as coisas mais interessantes. Vamos dizer que você não queira uma interface para os Observers. Que tal se qualquer classe que tiver o método receiveUpdate(s:Subject) pudesse ser um Observer?

Podemos usar Structural Typing para isso, e jogar a Trait Observer fora:

// Subject agora aceita qualquer observer que se "encaixa" no type definido
trait Subject {
  // Adicionei esse Type Alias para dar um nome a esse tipo estrutural.
  type Observer = { def receiveUpdate(s:Subject) }

  private var observers: List[Observer] = Nil
  def addObserver(observer: Observer) = observers = observer :: observers
  def notifyObservers() = observers.foreach(_.receiveUpdate(this))
}

// Não estende nada
object MyObserver {
  def receiveUpdate(s:Subject) {
    println("O subject mudou!")
  }
}

// O resto é igual
object MySubject extends Subject

MySubject.addObserver(MyObserver)
MySubject.notifyObservers

Structural Typing é parecido com Duck Typing, se o objeto tiver aquele método, ele é chamado. Por baixo dos panos o compilador resolve tudo usando Reflection, e por isso talvez tenha um impacto na performance do código.

Agora, uma instância de qualquer, QUALQUER classe que possua o método receiveUpdates(s:Subject) pode se adicionado como um Observer.

Legal, mas podemos ir além! E se eu não quiser que meu Observer seja uma instância de uma classe? Que tal se ele fosse apenas uma função?

Sinta o poder:

trait Subject {
  // Type Alias novamente. O tipo Observer é qualquer função que recebe Subject como parâmetro e não devolve nada (Unit é tipo void).
  type Observer = (Subject => Unit)

  private var observers: List[Observer] = Nil

  def addObserver(observer: Observer) = observers = observer :: observers

  // Mudei aqui para executar a função ( .apply aplica uma função )
  def notifyObservers() = observers.foreach(_.apply(this)) 
}

// Olha mãe, sou uma função!
val updateHandler = (s:Subject) => println("O subject mudou!")

object MySubject extends Subject

// Testa
MySubject.addObserver(updateHandler)
MySubject.notifyObservers

Type Alias é uma construção que permite que você defina um tipo. Isso mesmo, você declara um tipo e dá um nome para ele. Por exemplo, você pode dar um nome diferente para o tipo String:
type MinhaString = String.
Isso é bastante útil com Structural Typing.

Eu sei, eu sei, foi uma viagem e tanto. Se você nunca tinha experimentado nada do mundo funcional, esse aqui foi um pequeno exemplo.

Isso aí, vimos como em Scala podemos escrever o Observer de maneiras diferentes, tornando-o mais flexível.

Se o seu Observer não puder por algum motivo implementar uma interface, ou estender uma classe específica, podemos usar Structural Typing!

Se você não quer criar uma classe só para ser um Observer, e quiser só passar uma função de callback para seu Subject, agora você também pode de maneira simples e concisa. (em Java até daria para fazer uma classe anônima, mas convenhamos que assim é 3430943498 vezes mais bonito).

No próximo capítulo!

Arquitetura e Decoração com o Decorator

E não, não estamos falando sobre jardim, sofás, paredes e decoração de interiores…

View-first vs. MVC – Por que Lift é diferente?

Se você, caro leitor, desenvolve ou já desenvolveu aplicativos Web, deve estar cansado de ver essas três famigeradas letras: MVC, acrônimo de Model-View-Controller.

Se você programa em Java com Struts, sabe muito bem o que é uma Action.

Se você programa em Ruby com Rails, domina os ApplicationControllers.

Se você programa em PHP não tem idéia do que eu estou falando. Brincadeira! (perco o leitor mas não perco a piada).
Se você programa em PHP com CodeIgniter está bastante acostumado com os Controllers.

A arquitetura MVC reina absoluta entre os frameworks Web. Mas vamos lembrar porque é interessante fazer as coisas assim.

MVC e a separação de interesses

E o que essa arquitetura tem de tão fantástica? Ela simplesmente busca manter as coisas separadas. Modelo é modelo, apresentação é apresentação, não vamos bagunçar tudo em um grande bolo-fecal.ASP.
(Se você nunca mexeu em um grande arquivo .ASP de 3000 linhas cheio de HTML entremeado de lógica de negócio – e bugs -, e acha que ‘bolo fecal’ é ofensivo, é porque você tem sorte e não teve que ver essa nojeira!).

Apesar da quantidade de lógica que “vaza” para a camada de apresentação depender do framework que implementa o MVC, do engine de template que é usado e da disciplina do programador, o objetivo do MVC até que foi cumprido com sucesso.

Se você pegar uma aplicação razoavelmente bem feita, usando algum framework MVC poderá ver com clareza todas as classes do modelo, todas as classes que são os controller e todos os arquivos das Views.

De vez em quando, o desenvolvedor pode colocar uma lógica ou outra no template da View. Mas além disso, as coisas estão até que bem separadas.

O Lift ganha pontos aqui porque, ao contrário de PHP, ASP, JSP e outros engines de template, não é possível colocar lógica na View. Uma view em Lift é um simples XHTML e nada mais. É um modelo semelhante ao praticado pelo Apache Wicket.

MVC e uma lógica principal

Vamos pensar aqui, em linhas bem gerais, como é a fluxo de uma Request em um framework MVC:

  1. Chega uma Request para uma URL myApp/myController
  2. O framework mapeia essa URL para algum método do Controller MyController
  3. Esse método faz alguma lógica de negócio, e devolve algo que será no fim das contas a View
  4. A View, com base no estado do Model e do Controller mostra o que tem que mostrar na tela.

Perceba que a request no MVC está atrelada a uma lógica principal, que é basicamente o método invocado no Controller. Vamos tentar exemplificar.

Suponha que eu queira fazer uma tela para listagem de produtos e uma tela para edição de um produto específico. Eu teria as seguintes URLs.

/produtos/listar
/produtos/editar?prod=1234

E meu Controller teria dois métodos, um responsável por montar a tela de listar e o outro pela tela de editar.

Isso até que funciona muito bem, é fácil de fazer e de manter. O problema, é que atualmente, as aplicações Web estão ficando cada vez mais complexas, de modo que uma tela contém inúmeras funcionalidades concomitantes:

Uma tela de listagem de produtos, não apenas lista produtos. Na mesma tela temos uma lista de produtos no meio, um carrinho de compras mostrando o total de produtos na direita, uma lista de produtos sugeridos embaixo, e ainda um chat em tempo real no cantinho.

Já começa ficar complicado orientar nossa aplicação pelo Controller. Se pensarmos que o Controller vai se preocupar com a funcionalidade principal, temos que escolher uma para ele. Se escolhermos a listagem de produtos, quem vai cuidar da renderização das outras funcionalidades?

E ainda por cima, queremos manter nossas classes coesas e nossa View livre de lógica de negócio! Aí complicou.

Lift e a arquitetura “View First”

Foi pensando nesses cenários, muito comuns nas aplicações Web de hoje, que o criador do Lift, David Pollak, resolveu não seguir a abordagem MVC. Ao invés disso ele pensou em uma estratégia que é conhecida no meio como “View First”.

Nessa arquitetura o ponto de acesso de nossas páginas não é o Controller, mas a View. Pense assim, é a View que vai definir o que vai ter na tela e onde essas coisas vão aparecer.

Então cada um desses “blocos de funcionalidade” será mapeado para um Snippet. Cada pedacinho da tela será renderizado por um Snippet diferente, de uma maneira bastante componentizada.

Um Snippet é o equivalente Lift de um Controller, só que no caso é a View quem “chama” o Snippet, e não o contrário.

Vamos ver uma View em Lift, que nada mais é que uma página XHTML:

<!-- Topo -->
<div class="lift:Usuario.boasVindas">
 Bem vindo, <span id="usuarioLogado" />
</div>

<!-- Listagem de produtos -->
<div class="lift:Produtos.lista">
  <ul id="produtos">
    <li id="produtoItem">Produto1 - 9,99</li>
  </ul>
</div>

<!-- Chat Real Time em Comet -->
<div class="chatWindow">
  <div id="mensagens">
    <ul class="lift:comet?type=Chat">
      <li>Line 1</li>
      <li class="clearable">Line 2</li>
      <li class="clearable">Line 3</li>
    </ul>
  </div>
  <form class="lift:Form.ajax">
    <input class="lift:ChatIn" id="chat_in">
    <input type="submit" value="Chat">
  </form>
</div>

Observe que nessa View temos três funcionalidades bem distintas:

  • Um topo com uma mensagem personalizada para o usuário logado, que possivelmente pode ser reaproveitado em todas as telas do sistema.
  • Uma listagem de produtos.
  • Uma janelinha de Chat real-time usando Comet.

Essa View, que poderíamos chamar de /produto/lista.html agora é o ponto de entrada da nossa Request. A partir dessa View serão chamados três diferentes Snippets, cada um responsável por renderizar um pedacinho da View!

O interessante dessa abordagem “View First” é que podemos facilmente criar componentes utilizando os Snippets, e reaproveitá-los em diferentes Views.

Não vou focar muito nos Snippets agora, mas para você, curioso leitor, uma amostra do que poderia ser o Snippet de listagem de Produtos. Note como é sucinto, rápido, prático, bonito, elegante, charmoso, funcional etc etc :

object Produtos {
  def lista = {
    "#produtoItem" #> Produto.findAll.map(p => p.nome + " - " + p.preco)  
  }
}

A ligação com o elemento da view é feito por uma espécie de Seletor CSS. Nesse caso estamos pegando o <li id="produtoItem"> e repetindo para cada produto encontrado no modelo.

Moral da História

Nesse post relembramos um pouco da filosofia MVC e os motivos que levaram a sua concepção e aprendemos um pouco sobre a filosofia “View First” que é um dos fundamentos do framework Lift.

Para os iniciantes em Lift, essa mudança de filosofia pode ser meio estranha, podendo causar um pouco de confusão, desconforto, nojo, náuseas, já que a imensa maioria dos desenvolvedores Web esta bastante acostumada com o MVC.

Minha sugestão é: Faça um teste, brinque um pouco com o Lift para sentir um pouco essa arquitetura.

Você pode acabar gostando.

Como usar o SBT (simple-build-tool) com JRebel

Prólogo

Não muito tempo atrás, em um lugar não muito distante, chamado Web, existiam dois tipos de programadores:

  • Aqueles que dominavam a arte da linguagem interpretada, como Ruby, PHP. Acostumados com o tempo de Turnaround zero, em troca de menos performance.
  • E aqueles que dominavam a disciplina da linguagem compilada e tipada, como o Java, Scala e C#, que em troca de mais performance e servidores de aplicações, submeteram-se ao sofrimento do tempo de Turnaround

Um certo dia, um grupo de sábios estonianos uniram seus poderes e inventaram um artefato chamado JRebel que miraculosamente diminuía o tempo de Turnaround para os desenvolvedores Java e Scala.

Nota de rodapé do autor: Tempo de Turnaround é usado para denominar, entre outras coisas, o intervalo de tempo entre o momento em que você salva seu código e vê as alterações na tela. Em PHP esse tempo é zero, você salva o arquivo, faz refresh no browser e as mudanças estão lá. Em Java, caso você esteja desenvolvendo para um servidor de aplicação como o Weblogic, você tem que salvar seu código, compilar, empacotar, baixar e subir o Weblogic, aí sim você vê suas alterações.

Também conhecido como o tempo em que você levanta para pegar um cafézinho enquanto o servidor está subindo.

E assim eles destruíram os programadores de Ruby on Rails e viveram felizes para sempre.

Brincadeira.

Eles só ficaram mais produtivos, e passaram a tomar menos café (ou não).

JRebel, a salvação

Chega de historinha, esse é um post sério.

JRebel é uma ferramenta criada para diminuir o tempo que você espera para ver seu código funcionando.

Como ele faz isso?

Ele é um Java Agent, que altera os Classloaders da sua aplicação quando ela é executada. Esses classloaders modificados ficam monitorando os arquivos .class de sua aplicação. Quando algum desses .class muda, por exemplo quando você faz uma modificação e compila, o classloader percebe a mudança e recarrega a classe, sem que haja necessidade de reiniciar a aplicação ou o servidor.

Enquanto isso, no mundo dos builds

SBT, ou o Simple-Build-Tool

Essa ferramenta, escrita em Scala, não foi feita pelo Silvio Santos não.

Ela é uma alternativa para o famigerado Maven. Enquanto o Maven vem perdendo adeptos, por ser considerada uma ferramenta engessada, lenta, burra, o SBT vem arrasando corações no submundo emergente de Scala.

O foco aqui não é fazer uma apresentação detalhada de SBT, mas posso elencar alguns pontos chave:

  • É rápido.
  • A configuração é feita em Scala.
  • Integra com Maven e Ivy.
  • Dá para abrir um console Scala com todo seu projeto no Classpath para ficar testando coisas.
  • Consegue monitorar seu código fonte, executando o build assim que algum arquivo muda.

Se você programa em Scala e nunca usou o SBT experimente.

SBT e JRebel, a combinação ágil

Essa combinação vem ficando cada vez mais famosa, especialmente entre os programadores Lift. E tenho que confessar, funciona muito bem. Meu tempo de turnaround está praticamente zero.

Como configurar?

Primeiro baixe o JRebel e em seguida peça uma licença gratuita de um ano para desenvolvedores Scala (ao lado direito da página).

Quando receber a licença siga as instruções (colocar o arquivo da licença no diretório do JRebel).

Em seguida baixe o JAR do SBT, coloque em uma pasta qualquer e crie um script que irá chamá-lo:

java -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=516m -Xmx512M -Xss2M -noverify -javaagent:[CAMINHO_DO_JREBEL.JAR] -jar `dirname $0`/[CAMINHO_DO_JAR_DO_SBT] "$@"

Note que estamos informando ao Java que o JRebel é um javaagent. Assim, quando o você executar o SBT usando esse Script, o JRebel vai subir junto.

Coloque esse Script no seu PATH.

Como usar?

Para tirar os proveitos do JRebel você precisa criar uma aplicação em SBT, usar uma aplicação em SBT, ou configurar o SBT em uma aplicação Maven:

Se você usa Maven, saiba que o SBT consegue ler as dependências do arquivo pom.xml caso você não especifique nenhuma dependência na classe de configuração do SBT. Isso é bastante útil se você precisa manter um projeto em Maven e ao mesmo tempo quiser tirar proveito do SBT.

Vejamos um exemplo. Vamos usar um projeto Lift de exemplo que coloquei no Github:


git clone git://github.com/felipekamakura/sbt_jrebel_lift_example.git myApp


> cd myApp
> sbt


#############################################################

JRebel 3.5 (201011151605)
(c) Copyright ZeroTurnaround OU, Estonia, Tartu.

Over the last 23 days JRebel prevented
at least 92 redeploys/restarts saving you about 3.7 hours.

This product is licensed to Personal
until April 3, 2011
for up to developer seats on site.

The following plugins are disabled at the moment:
* JBoss AOP Plugin (set -Drebel.jbossaop_plugin=true to enable)
Integration with jboss aop agent
* Jackson Plugin (set -Drebel.jackson_plugin=true to enable)
Supports reloading Jackson's JsonSerializer caches.
* Lift Plugin (set -Drebel.lift_plugin=true to enable)
Supports reloading singleton objects that extend LiftScreen or Wizard.
* Log4j plugin (set -Drebel.log4j-plugin=true to enable)
Reloads full configuration of log4j
* RESTEasy Plugin (set -Drebel.resteasy_plugin=true to enable)
Supports adding/changing methods with @Path annotation for RESTEasy application.
* Seam Wicket Plugin (set -Drebel.seam_wicket_plugin=true to enable)
Integration with load time weaving seam annotations to wicket classes
(-javaagent:)
* WebObjects Plugin (set -Drebel.webobjects_plugin=true to enable)
WebObjects JRebel Plugin

#############################################################

Getting Scala 2.7.7 ...
:: retrieving :: org.scala-tools.sbt#boot-scala
confs: [default]
2 artifacts copied, 0 already retrieved (9911kB/5097ms)
Getting org.scala-tools.sbt sbt_2.7.7 0.7.4 ...
:: retrieving :: org.scala-tools.sbt#boot-app
confs: [default]
15 artifacts copied, 0 already retrieved (4096kB/13414ms)
[info] Recompiling project definition...
[info] Source analysis: 1 new/modified, 0 indirectly invalidated, 0 removed.
Getting Scala 2.8.1 ...
:: retrieving :: org.scala-tools.sbt#boot-scala
confs: [default]
2 artifacts copied, 0 already retrieved (15118kB/805ms)
[info] Building project Lift SBT Template 0.1 against Scala 2.8.1
[info] using LiftProject with sbt 0.7.4 and Scala 2.7.7
>

Você está agora no console do SBT. Execute o comando update para baixar as dependências do projeto:

>update

Em seguida, compile e suba a aplicação exemplo com o comando jetty-run:

>jetty-run

Legal, se tudo deu certo, a aplicação Lift de exemplo está disponível em http://localhost:8080/. Entre lá para dar uma olhada, deve estar assim:
Aplicação Lift, JRebel e SBT

Vamos agora ver o poder do JRebel alterando uma classe, compilando-a e vendo a mudança na tela:

Execute o seguinte comando no SBT:

>~prepare-webapp

O prepare-webapp é um comando do SBT que compila e empacota a sua aplicação web. O ~ (til) antes do comando fala para o SBT – “rode o seguinte comando sempre que houve uma mudança no código fonte”.

Sempre que você usar o ~ (til) no SBT, ele vai ficar em um “estado de alerta”, e vai construir tudo sempre que houver mudança. Isso é essencial para conseguirmos baixar o tempo de Turnaround. E também o SBT é esperto o suficiente para compilar apenas as classes de seu projeto que foram alteradas.

Agora abra e altere o arquivo src/main/scala/code/snippet/HelloWorld.scala. Troque a frase de saudação para sua frase favorita.

class HelloWorld {
// Altere a linha abaixo para sua frase favorita
def howdy = "#greet *" #> "Cai fora curioso."
}

Então, quando você salvar a classe, o SBT vai começar a compilar tudo e o JRebel vai notar que houve mudança no .class dessa classe.

De um refresh no browser e note que a tela foi atualizada com suas modificações! Rápido, prático, indolor! Veja que a frase mudou:
Aplicação depois da alteração

É só isso pessoal! Nada de intervalo para o cafézinho durante o build, foi mal…