Groovy Dinâmico

Fato: mais da metade das pessoas que conheço e programam em Groovy nunca usaram invocação dinâmica de métodos simplesmente por não saber o que é ou simplesmente como funciona. Sendo assim, sem mais delongas, vamos por a mão na massa:

Vamos supor que exista a classe PatoLouco implementada em Java tal como no código abaixo:


class PatoLouco {

public void digaQua() {

System.out.println("Qua!");

}

public void digaQuaQua() {

System.out.println("Qua Qua!");

}

}

Em Java, se quisermos invocar um dos métodos presentes na classe, temos de satisfazer um requisito básico: ele deve estar implementado, pois isto é verificado em tempo de compilação pela linguaem. De acordo com este ponto de vista, temos duas alternativas: ou a própria classe já implementa estes métodos OU a classe implementa uma interface como a descrita abaixo:


public interface Pato {

public void digaQua();

public void digaQuaQua();

}

/*

E seguindo esta lógica, poderemos ter zilhões de tipos diferentes de patos, tal como a classe abaixo.

*/

public class PatoNaoTaoLouco implements Pato {

public void digaQua() {

System.out.println("um qua não tão louco assim");

}

public void digaQuaQua() {

System.out.println("Qual a razão de ter de dizer dois 'quas'?");

}

}

A presença das interfaces nos garante que os métodos serão implementados pelas nossas classes do tipo Pato. Se em um futuro não tão distante quanto parece formos preguiçosos em nossa modelagem e quisermos lidar com galinhas e outros tipos de aves, iriamos criando nossas interfaces, até chegar a um ponto no qual teriamos uma classe tal como a abaixo:


public class AveOrnitorrinca implements Pato, Galinha, Avestruz, Ganso, Aguia, Marreco {

// zilhões de métodos implementados de acordo com as interfaces

}

Linguagens dinâmicas como Groovy resolvem este problema aplicando o “princípio do pato”: se anda como um pato, corre como um pato e ‘fala’ como um pato, é porque é um pato.  Sendo assim, vou apresentar um dos exemplos mais batidos deste principio no código Groovy abaixo:


class Cachorro {

def incomode() {println "Lato sem parar. Au au auuuuu!"}

}

class Gato {

def incomode() {println "Te arranho sem parar. Grite!"}

}

// repare que não há aplicação de herança ou interfaces.
// o código abaixo executará normalmente

instancias = [new Gato(), new Cachorro()]

for (instancia in instancias) {

instancia.incomode()

}

A saída que teremos será:


Lato sem parar. Au au auuuuu!
Te arranho sem parar. Grite!

Uma solução muito mais limpa do que em Java não é mesmo? Isto porque a verificação dos métodos é feita em tempo de execução. Na realidade, é possível ir além. Observe o código abaixo:


class CachorroLouco {

def lata() {println "Au!"}

def deite() {println "Deitado"}

def role() {println "Rolando!"}

def digaIsto(isto) {println isto}

}

/* Criei uma matriz de strings contendo os nomes dos métodos acima*/
nomeDosMetodos = ["lata", "deite", "role"]

/* E agora, um pouco de "mágica" */

cao = new CachorroLouco()

for (metodo in nomeDosMetodos) {

cao."${metodo}"()

}

// Claro, o código abaixo também é válido

cao."lata"()

// E com parâmetros, seria a mesma coisa:

cao."digaIsto"("Isto!")

Não é legal? Passando uma string para a minha instância, em tempo de execução eu posso invocar métodos dinâmicamente, o que abre uma gama imensa de possibilidades.

O que acontece se o método não existir?


new CachorroLouco()."faca_algo_impensado"()

// Groovy me dará esta saida:

No signature of method: CachorroLouco.faca_algo_impensado() is applicable for argument types (...)

Uma excessão do tipo groovy.lang.MissingMethodException será disparada. No entanto, podemos resolver este problema sobrescrevendo o métodomethodMissing em nossa classe, tal como no exemplo abaixo:


public class CachorroLouco {

def methodMissing(String name, args) {
println "O método ${name} não foi implementado seu perdido!"
}

//  restante da classe abaixo

}

Quando um método não é encontrado em uma classe, Groovy irá executar este método que, se for sobrescrito, poderá lidar com o seu caso específico. E neste momento você me pergunta: e se for uma classe implementada em outra linguagem, como Java, por exemplo: como lidar com esta situação?

Ai entra a nosso amigo (ou seria amiga?) ExpandoMetaClass

Groovy nos permite incluir novos comportamentos em classes já existentes. Fazemos isto usando a meta classe ExpandoMetaClass, que nos permite adicionar novos métodos, construtores e propriedades usando a sintaxe da closure que já conhecemos.

Sendo assim, o código abaixo é perfeitamente válido:


String.<strong>metaClass</strong>.sempreImprimaIsto = { ->

println "Sempre imprimirei isto"

}

"sou uma nova string".sempreImprimaIsto() //método incluido na hora.

// Lembra dos codecs de string do Grails? Funcionam exatamente assim.

Em nosso caso, bastaria injetar o método methodMissing em nossa classe CachorroLouco, exatamente como no exemplo abaixo:


CachorroLouco.metaClass.methodMissing = {String methodName, args ->
println "O método não existe"
}

É ou não é MUITO legal?

14 thoughts on “Groovy Dinâmico

  1. Muito bacana groovy! O guia de grails/groovy tá muito show! Fico lendo e não consigo mais parar! =D
    vlw!

    Responda

    admin Reply:

    Que bom Eduardo! Valeu!
    Farei o possível para te manter ainda mais viciado então! :)

    Responda

  2. Muito bom,

    só uma problema na exibição de um código:

    String.metaClass.sempreImprimaIsto

    Responda

    admin Reply:

    Que bom que gostou!

    Infelizmente é comum ocorrer algum erro de digitação. Tenho me esforçado ao máximo para que isto não ocorra tanto mais. Nos próximos repare que são BEM mais raros (espero!). :)

    Responda

  3. Fundamental esse negócio de invocar métodos dinâmicamente. Realmente abre uma gama imensa de possibilidades.

    Parabéns.

    Responda

  4. Excelente dica!!!

    Como muitos dos que estão começando em Groovy vêm do Java, geralmente nem sonham com este maravilhoso recurso. Achei massa d+

    Groovy na veia!!!

    Responda

  5. Estou utilizando Groovy achei interessantes mas essa do type duking me parece furada.

    Ok não precisa implementar o método mas por outro lado vc passa uma lista de objetos que vc sabe que tem aquele método.

    Não parece ter nada de mágico a não ser o fato da liguagem ser tipificada dinamicamente. (Isso sim que acho que é o princípio do pato).

    Fato dele navegar para seu objeto interior a partir de um genérico é muito bom.

    Responda

  6. Crie um método bonifica que aumenta o salário do funcionário de acordo com o parâmetro passado como argumento. Crie também um método calculaGanhoAnual, que não
    recebe parâmetro algum, devolvendo o valor do salário multiplicado por 12.

    Responda

  7. Crie um método mostra( ), que não recebe e nem devolve parâmetro algum e imprime
    todos os atributos do nosso funcionário.

    Responda

Leave a Reply

Your email address will not be published. Required fields are marked *