Groovy Power: Flexibilidade, Simplicidade e Código Legível

Eu um post anterior comentei sobre a importância de se escrever um código mais fluente, ligível e auto-explicativo. Muitas vezes são com pequenas ações que podemos melhorar este aspecto sobre o código que escrevemos. Estas pequenas ações são caracterizadas com um bom disgn coeso e pouco acoplado, bons nomes de classes e métodos, variáveis com nomes significativos e código fluente e claro.

Criar código fluente e claro pode ser especialemte desafiador. É facto que certas linguagens facilitam ou dificultam esta tarefa, Java não é a linguagem mais fluente do mundo mas existem alternativas. No post anterior eu ensinei a criar uma DSL com Java sem nenhum framework e com batsante fluencia.

Neste post quero comentar sobre a linguagem dinamica Groovy que é a referência padrão de linguagens dinamicas para a JVM. Existem outras bem funcionais e interessantes como JRuby, BeanShell, Jython, Scala e tantas outras. Em especial hoje vou falar de Groovy, por que?

Por que como desenvolvedor Java este foi o primeiro contato com linguagens dinamicas sobre a JVM. Vejo de uma forma muito natual que o desenvolver nos dias de hoje busque outras soluções que não sejam unicamente dentro da linguagem Java. Mas de certa forma é muito claro para mim que utilizar a JVM é um ótima idéia, usar linguagens dinamicas que rodam em cima da JVM e tem integração com Java e os principais Frameworks existentes em Java com Spring e Hibernate é melhor ainda, este é o caso do Groovy.



Groovy é um linguagem que roda em cima da JVM, então é claro e obvio que ela não é a linguagem mais performatica do mundo, mas muitas vezes o problema esta em outro lugar, na persitência, no servidor, enfim... não é o fato da linguagem ser interpretada que deixa o uso da mesma inviável.

Por sinal o Groovy pode ser usando tanto interpretado como um script ou pode ser compilada pelo groovyc e rodar como uma class Java normal. Java é OO e Groovy também. Porem o Groovy é hibrido e você pode programar de forma procedural, o que de facto é muito útil para construção de scripts e na própria produtividade.

Groovy possui várias vantagens entre elas:
  • Suporte nativo a Closures
  • DRY
  • Duck Typing
  • GStrings
  • Suporte nativo a Mapas e Listas
  • Novas funcionalidades na API do Java
  • Sobrecarga de operadores
  • Operações Null-safe
Suporte nativo a Closures: Closures são uma especie de função mas livre de váriaveis. Você pode passar váriaveis para um closure mais isso não esta previamente definido na assinatura da closure(comparando com um método) estas variáveis ficam no corpo da implementação da closure. A idéia de Closures não é nada nova é de 1960 que por sinal já estava presente em linguagens como o Lisp.

<br /> def somar = { va , vb -> <br />     println va + vb <br /> } <br /> <br /> somar 1, 2<br />

Este código a cima mostra um exemplos simples de Closure em Groovy. Se você executar este código no groovyConsole por exemplo vai aparecer 3 no console como resultado.

DRY:Don't Repeat Yourself é um conceito bem interessante que as novas soluções vem implementado. Este conceito se baseia em elimitar a duplicação em todos os sentidos. Em java sofremos muito com isso quando usamos diversos frameworks baseados em XML(ex: clássico Spring, Hibernate, Struts, web.xml, etc..). Mas este conceito não se aplica apenas a parte de configuração. Este conceito aparece no The Pragmatic Programmer. Bom se você não leu este livro ainda não perca tempo e compre agora e leia o quanto antes. Neste caso foco em DRY por que isto leva a um código mais legível também e isso também foi incorporado ao Groovy.

<br>package com.blogspot.diegopacheco.javapain;<br><br>public class Pessoa{<br>    <br>       private String nome;<br>       private Integer idade;<br>       <br>       public Pessoa(){<br>       <br>       }<br>       <br>       public Pessoa(String nome,Integer idade){<br>          this.nome  = nome;<br>          this.idade = idade;<br>       }<br>       <br>       public String getNome(){<br>          return nome;<br>       }<br>       <br>       public void setNome(String nome){<br>          this.nome = nome;<br>       }<br>       <br>       <br>       public Integer getIdade(){<br>          return idade;<br>       }<br>       <br>       public void setIdade(Integer idade){<br>          this.idade = idade;<br>       }<br>       <br>       public String toString(){<br>          return "Nome:" + nome + ", idade: " + idade;<br>       }<br>        <br>    }<br>

Este é um tipico código que escrevemos em Java. Não vou entrar em discussões mais DDD, modelo anemico e tudo mais, isso é assunto para outros posts, mas. Mesmo com DDD ainda existe a possibilidade de não existirem validações e código de negocio nesta classe. Muitas vezes fazemos métodos getters and setters mas sem código dentro.

Este código acima é Java, mas funciona perfeitamente em Groovy. Porem em Groovy poderiamos fazer algo assim por exemplo:
<br>public class Pessoa{<br>    <br>   def String nome;<br>   def Integer idade;<br>   <br>   public Pessoa(){<br>   <br>   }<br>   <br>   public Pessoa(String nome,Integer idade){<br>      this.nome  = nome;<br>      this.idade = idade;<br>   }<br>   <br>   public String toString(){<br>      return "Nome:" + nome + ", idade: " + idade;<br>   }<br>    <br>}<br><br>Pessoa p = new Pessoa();<br>p.setNome("Diego Pacheco");<br>p.setIdade(25);<br>System.out.println(p);<br>

Ok. Mas cade os getters e setters? Isto compila? Roda sem levantar Exceptions? Sim compila e roda sem exceptions, funciona exetamente igual ao código acima, porém não precisamos escrever os códigos de getters e setters, já que não temos regras nem validações lá. Isso pode ficar ainda melhor veja o outro código Groovy a baixo:

<br>public class Pessoa{<br>    <br>   def nome, idade;<br><br>   public String toString(){<br>      return "Nome:" + nome + ", idade: " + idade;<br>   }<br>    <br>}<br><br>p = new Pessoa(nome: "Diego", idade: 25);<br>println p<br>

Cade o código dos construtores? Como que isso funciona? O Groovy resolve estas questões para nos, tirando algumas coisinhas chatas da linguagem Java e nos dando mais produtividade e legibilidade. Mas vamos melhorar mais? É possível?
<br>public class Pessoa{<br>    <br>   def nome, idade;<br><br>   String toString(){<br>      "Nome: ${nome} idade: ${idade}"<br>   }<br>    <br>}<br><br>p = new Pessoa(nome: "Diego", idade: 25)<br>println p<br>

Cade o return? E outra coisa onde estão as concatenações de Strings usando o operador "+"? Bom quanto ao return ele é opcional, o Groovy assume que a ultima coisa que você escreveu é o retorno. Quanto as Strings talvez que tenha sido programador PHP como eu pode entender melhor. Este é o recurso chamado de GStrings. Que tal um pouco mais em ? Vamos lá então.

<br>class Pessoa {<br>  def nome, idade;<br>}<br><br>Pessoa.metaClass.toString = {<br>   return "Nome: ${nome} idade: ${idade} . executou"<br>}<br><br>println new Pessoa(nome: "Diego", idade: 25).toString()<br>

Agora a classe Pessoa só tem atributos, desta vez estou adicionado um método a uma classe existente(Pessoa) em tempo de execução. Eu estou implementado o método toString e acreditem funciona. Como é possível? Este é o Duck Typing em ação. Vamos ver mais...

<br>def toString = { nome, idade -><br>   println "${nome} - ${idade}"<br>}<br><br>toString "Diego", 25<br>

Agora eu peguei o mesmo código que estava em uma classe e simplifiquei mais ainda. Como ? Colocando todo o código em uma Closure. Outra coisa legal é que o ";" e os "(" e ")" são opcionais pode colocar ou não que vai funcionar.

Duck Typing: Na programação fortemente tipada como é no caso do Delphi, C e Java. Ganhamos com algumas coisas mas perdemos em flexibilidade e muitas vezes o código é mais massivo e menos legível, você costuma ter que escrever mais para dizer as suas intenções a linguagem. Com tipagem forte os métodos de um objeto são definidos pelas sua classe superior(herança) e suas interfaces(além dos próprios métodos) e isto é fixo de certa forma. Com o Duck type isto depende do estado corrente do objeto. Isso mesmo pode ser que hora um objeto Pessoa tenha 10 métodos e pode ser que em outra hora ele tenha 15.

GStrings: É outro recurso fantastico do Groovy que da muita produtividade e simplicidade ao nosso código. Este é o recurso de ter String no stilo PHP. Você usa o operador "$" com "{}" e pode colocar uma váriavel ou expressão complexa dentro das chaves e ele resolve isso tudo dentro da mesma String. Vamos ver mais um exemplo de código:

<br>def x = 10;<br>println "Hoje eh: ${new java.util.Date().toString()} - ${x}"<br>

Como você pode ver neste código estou usando uma classe do Java no meio da GString e também resolvendo uma variável Groovy. Neste caso a ligibilidade do código fica muito maior também, mais simples e mais divertido. :)

Suporte nativo a Mapas e Listas: Mais um grande recurso do Groovy. Acredito que o próprio sub-titulo ja explique por si só o que isso significa, mas vamos ver alguns códigos em Groovy para sentir na prática do que eu estou falando.

<br>def lista = [1,2,3,4,5,"Diego",'D',true,new Object(),{}]<br>lista.each {<br>   println it<br>}<br>

Neste código a cima estamos criando uma lista sem ter que instancia uma classe diretamente, isso é feito pelo Groovy. Depois estou usando o método each que a lista tem e passo um closure para que ele execute a cada item que existe na lista. A viariável implicita *it* representa a variável corrente neste caso o elemento da lista que esta sendo iterado.

<br>def lista = [1,2,3,4,5,"Diego",'D',true,new Object(),{}]<br><br>lista += 6  // adiciona o item 6 a lista<br>lista -= 1  //  remove o item 1 da lista <br>lista << 7  // adiciona o item 7 a lista<br></p><p><br>lista.findAll { it == "Diego" }.each {<br>   println it<br>}<br>

Neste código estou aidionando e removendo itens a lista, no final faço um código para procurar na lista um elemento chamado "Diego" e percorendo este resultado e imprimindo na tela no final. Vamos ver o suporte a mapas agora.

<br>def mapa = [1: "Diego", 2: "Bruna", 3: "Pog"]<br>mapa.each{ k, v -><br>   println "Chave: ${k} - valor: ${v}"<br>}<br>

Neste código criei um mapa e adicionei as chaves 1, 2 e 3 com os valores Diego, Bruna e Pog. Depois usei a função each e percorri todo o mapa mostrando as chaves e valores. Vamos ver mais algumas coisas de mapas de forma diferetne agora:

<br>def mapa = [1: "Diego", 2: "Bruna", 3: "Pog"]<br><br>mapa += [4: "teste"]<br><br>mapa.each{<br>   println "Chave: ${it.key} - valor: ${it.value}"<br>}<br>

Estou adicionando um item ao mapa de maneira muito fácil e igual a da lista e desta vez estou percorrendo o mapa com o *it* implicito do Groovy.

Novas funcionalidades na API do Java: Bom algumas coias vocês já viram como os métodos *each*, *findAll*. Existem outros métodos bem úteis como por exemplo: Quando você trabalha com File existe o método eachFile para percorrer todos os Files de um File pai. Confira também as seguintes formas de loop:

<br /> array = (0..4).toArray()<br /> x = 0<br /> for ( i in array ) {<br /> x += i<br /> }<br /> <br /> x = 0<br /> for ( i in [0, 1, 2, 3, 4] ) {<br /> x += i<br /> }<br /> <br />

Outra coisa legal é o Switch para Strings, confira o código Groovy a baixo:
<br>String nome = "Diego"<br>switch(nome){<br>   case "joao":<br>         println "teste 1"<br>   break<br>   case "Diego":<br>        println "teste 2"          <br>   break<br>}<br>

Sobrecarga de operadores: É um dos recursos mais fantasticos do Groovy. Com isso a legibilidade e fluencia ficam em alta mesmo. Isso pode ser feito não apenas com numeros mas com qualquer classe. Veja o código a baixo:

<br>class Pessoa{<br>   def nome, namorado<br>   <br>   def leftShift(n){<br>      namorado = n<br>   }<br>   <br>   def mostrarDados(){<br>      println "Nome: ${nome} Namora: ${namorado.nome}"<br>   }<br>}<br><br>def p1  = new Pessoa(nome: "Diego")<br>def p2  = new Pessoa(nome: "Bruna")<br><br>p1 << p2<br>p1.mostrarDados()<br>

Operações Null-safe: Quem gosta de ver um bom NPE(Null Pointer Exception) na tela? A solução para isso é fazer diversos ifs para testar o estado dos objetos antes de acessar métodos e própriedades do mesmo. Porem com Groovy isto não é necessário por que existe um operador para isso, basta colocar o "?" na frente que tudo se resolve.

<br>class Pessoa{<br>  def show = {<br>     println "groovy show"<br>  }<br>}<br><br>def p = new Pessoa()<br>def x = null<br><br>p.show()<br>x?.show()<br>

Bom pessoal e existe muito mas mauito mais. O Legal do Groovy é isso estas novas funcionalidades, facilidades, integração com Java. Basta colocar um jar na sua aplicação e pronto você já pode aproveitar tudo isso.

Fiz uma apresentação sobre este assunto, confira no meu slide-share:




Se você quizer saber mais sobre Groovy pode conferir estes posts aqui:
Bom Espero que tenham gostado.

Abraços e até a Próxima.

Popular posts from this blog

Telemetry and Microservices part2

Installing and Running ntop 2 on Amazon Linux OS

Fun with Apache Kafka