Creación de Builders y DSL's con Groovy

  • 548 views
Uploaded on

Una breve introducción a la Metaprogramación, sus características y como nos ayudamos de ella para crear Builders y DSL's con Groovy

Una breve introducción a la Metaprogramación, sus características y como nos ayudamos de ella para crear Builders y DSL's con Groovy

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
548
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
13
Comments
0
Likes
1

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide































Transcript

  • 1. Creación de builders y DSL's con Groovy @neodevelop - @synergyj
  • 2. Agenda Groovy Metaprogramación Builders DSL
  • 3. {}
  • 4. {}
  • 5. Metaprogramación • Programas que escriben programas... • Soporte para agregar métodos y propiedades a un objeto en tiempo de ejecución... • Soporte de intercepción de llamadas, como AOP
  • 6. Meta-object protocol “Interpreta la semantica de un programa abierto y extensible. Determina que significa un programa y que comportamiento tiene, así también, maneja objetos que manipulan, crean, describen o implementan otros objetos” En Groovy podemos usar el MOP para invocar métodos dinámicamente y sintetizar clases y métodos al vuelo
  • 7. MOP de Groovy Todos los accesos a los métodos, propiedades, constructores, operadores, etc., pueden ser interceptados El comportamiento en Java esta fuertemente atado al tiempo de compilación, en Groovy, el comportamiento es adaptable en tiempo de ejecución
  • 8. Meta-object Protocol GroovyObject En Groovy se trabaja con 3 tipos de objetos: POJOs POGOs Groovy Interceptors
  • 9. GroovyObject package groovy.lang; public interface GroovyObject { Object invokeMethod(String name, Object args); Object getProperty(String propertyName); void setProperty(String propertyName, Object newValue); MetaClass getMetaClass(); void setMetaClass(MetaClass metaClass); }
  • 10. MetaClass public interface MetaClass extends MetaObjectProtocol { Object invokeMethod(...); Object getProperty(...); void setProperty(...); Object invokeMissingMethod(...); Object invokeMissingProperty(...); Object getAttribute(...); void setAttribute(...); void initialize(); List<MetaProperty> getProperties(); List<MetaMethod> getMethods(); ClassNode getClassNode(); List<MetaMethod> getMetaMethods(); int selectConstructorAndTransformArguments(...); }
  • 11. Intercepción de métodos - GroovyInterceptable // Definamos una clase que implemente GroovyInterceptable class ClaseInterceptada implements GroovyInterceptable{ def doMetodo(param){ "Hola $param" } //Tenemos que implementar este metodo de la interfaz def invokeMethod(String nombre, args){ System.out.println "Ejecutando el metodo '$nombre' con argumentos '$args'" //Obtenemos el metodo a ejecutar de la clase def metodoValido = ClaseInterceptada.metaClass.getMetaMethod(nombre,args) //Si encontro el metodo a ejecutar... if(metodoValido != null){ //Lo invocamos desde el metodo que encontro metodoValido.invoke(this, args) }else{ //Si no lo encuentra simplemente llamamos al mŽtodo convencionalmente ClaseInterceptada.metaClass.invokeMethod(this, nombre, args) } } } def c = new ClaseInterceptada() println c.doMetodo("@grailsmx")
  • 12. Intercepción de métodos - ExpandoMetaClass //Ahora usemos un interceptor en una clase que no es de nosotros Float.metaClass.invokeMethod = { String nombre, args -> //Al igual desplegamos algo de informacion System.out.println "Ejecutando el metodo '$nombre' con argumentos '$args'" //Obtenemos el metodo del metaClass def metodoValido = Float.metaClass.getMetaMethod(nombre,args) //Si no existe dicho metodo if(metodoValido == null){ //Entonces regresamos la ejecucion convencional return Float.metaClass.invokeMissingMethod(delegate,nombre,args) } //Invocamos al metodo original con sus parametros resultado = metodoValido.invoke(delegate,args) //Regresamos la ejecucion del metodo resultado } //Usemos los metodos de la clase que no es propietaria println 10F.intValue() println 100F.toString() try{ 50F.empty() }catch(Exception e){ println e.message }
  • 13. MOP - Inyección de métodos(Categorías) //Definimos la clase que permitira ser la categoria class VerificaGramatica{ //Para que un metodo pueda ser categorizado, debe ser static def static esPalindrome(String frase){ //Implementamos nuestra categoria if(frase == new StringBuilder(frase).reverse().toString()) true else false } } //Con ayuda de la palabra reservada 'use' aplicamos la categoria use(VerificaGramatica){ def frase = "anitalavalatina" println frase.class.name println "Es palindrome?: " + frase.esPalindrome() println frase.class.name }
  • 14. MOP - Inyección de métodos(ExpandoMetaClass) //Podemos inyectar métodos estaticos Integer.metaClass.esPar = { -> delegate % 2 == 0 } //Probemos nuestro método estatico println "2 es Par? " + 2.esPar() println "3 es Par? " + 3.esPar()
  • 15. MOP - Síntesis de métodos(MethodMissing) class Persona{ String nombre Map relaciones = [:] def methodMissing(String relacion, personas){ if(relaciones.containsKey(relacion)){ personas.each{ persona -> relaciones.get(relacion).add(persona) } }else{ relaciones.put(relacion,personas as List) } } } def juan = new Persona(nombre:'Juan') juan.trabajaEn("SynergyJ") juan.trabajaCon("Manuel","Jorge") juan.esAmigoDe("Domingo") juan.trabajaCon("Andres") juan.esAmigoDe("George") juan.conoceA("Susana","Perla","Cassandra","Alejandra") println juan.relaciones
  • 16. MOP - Síntesis de métodos(ExpandoMetaClass) import java.text.NumberFormat def valoresDeConversion = ['USD':0.07973, 'EUR':0.0644, 'GBP':0.0538, 'JPY':7.2361] BigDecimal.metaClass.methodMissing = { String methodName, args -> tipoDeConversion = methodName[2..-1] valorDeConversion = valoresDeConversion[tipoDeConversion] if(valorDeConversion){ NumberFormat nf = NumberFormat.getCurrencyInstance(Locale.US) nf.setCurrency(Currency.getInstance(tipoDeConversion)) return nf.format(delegate * valorDeConversion) } "No hay conversion de Pesos a ${tipoDeConversion}" } println 13.00.enUSD() println 16.00.enEUR() println 1.00.enXYZ()
  • 17. Creación dinámica de clases con Expando def file = new File("migracion.csv") /* * Empresa,Nombre,Apellido,Puesto,correo... * 42 Claps,CŽsar,Salazar Hern‡ndez,CEO,c@42claps.com... * 4D Hispano,Dominique,Coste,Country Manager,d@4dhispano.com... */ def sql = groovy.sql.Sql.newInstance("jdbc:hsqldb:file:/devDB","sa","","org.hsqldb.jdbcDriver") lines = file.readLines() atributos = lines[0].tokenize(",") //Creaci—n de Expando def persona = new Expando() atributos.each{ //Creando atributos con Expando, tmb se pueden crear mŽtodos persona."${it.toLowerCase()}" = "${it.toLowerCase()}" } def insert = "insert into user(VERSION,PASSWD,ENABLED,USERNAME,EMAIL_SHOW,EMAIL,USER_REAL_NAME) " insert += "values(0,'7c4a8d09ca3762af61e59520943dc26494f8941b',true,?,true,?,?)" lines.each{ line -> valores = line.tokenize(",") index = 0 atributos.each{ persona."${it.toLowerCase()}" = "${valores[index]}" index++ } //Uso de Expando sql.execute(insert,[persona.correo,persona.correo,"${persona.nombre} ${persona.apellido}"]) }
  • 18. ¿Qué es un builder?
  • 19. Builders Son DSL’s internos que proveen trabajar facilmente con ciertos tipos de problemas De entrada, si tenemos la necesidad de trabajar con ciertos tipos de estructuras o representaciones, los builders son la mejor opción Proveen una sintaxis que no ata con dicha estructura o implementación Solo son fachadas, por que en realidad no están reemplazando la implementación, solamente proveen una manera elegante de usarla Groovy ya provee algunos, pero podemos hacer los propios
  • 20. Metaprogramación Builders BuilderSupport
  • 21. //Instanciamos un builder sqlBuilder = new MiSqlBuilder() //Ejecutamos nuestro builder sqlBuilder.build{ selecciona("campo1","campo2","campo3") tabla("nombreDeTabla") donde("1=1") insertar('tabla',['campo1','campo2','campo3'],['valor1','valor2','valor3']) ultimoInsert }
  • 22. class MiSqlBuilder{ def result = new StringWriter() def build(closure){ closure.delegate = this closure() println result } def methodMissing(String name,args){ switch(name){ case 'selecciona': result << "nSELECT ${args.join(',')}" break case 'tabla': result << " FROM ${args.join(',')}" break case 'donde': result << " WHERE ${args[0]}n" break case 'insertar': result << "nINSERT INTO ${args[0]}(${args[1].join(',')}) " result << "VALUES('${args[2].join('','')}')n" break } } def propertyMissing(String name){ if(name=="ultimoInsert"){ result << "nSELECT last_insert_id() n" } } }
  • 23. Lenguajes de dominio específico
  • 24. DSL Están enfocados a un cierto tipo de problema La sintaxis está orientada al negocio(hay expertos) No lo usamos para resolver problemas de propósito general como lo haría Java Es pequeño, simple, expresivo y enfocado a cierta área Son manejados por el contexto y elocuentes Con ayuda del MOP podemos crear DSL’s
  • 25. ¿Cómo desarrollar un DSL? Tipado dinámico y opcional La facilidad de usar Scripts ExpandoMetaClass Closures Sobrecarga de operadores Soporte de Builders Work-around de paréntesis
  • 26. tomar 4.pastillas, de: lsd, en: 12.horas
  • 27. class Droga { String nombre String toString() { nombre } } class CantidadDeDroga { int cantidad String toString() { cantidad == 1 ? "1 pastilla" : "$cantidad pastillas" } } class TiempoDeDuracion { Number numero String unidad } Integer.metaClass.getPastillas = { -> new CantidadDeDroga(cantidad: delegate) } Number.metaClass.getHoras = { -> new TiempoDeDuracion(numero: delegate, unidad: "horas") } def tomar(Map m, CantidadDeDroga cdd) { println "Tomar $cdd de $m.de en $m.en.numero $m.en.unidad" } def oxicodona = new Droga(nombre: "Oxicodona") def lsd = new Droga(nombre: "LSD") tomar 2.pastillas, de: oxicodona, en: 6.horas tomar 4.pastillas, de: lsd, en: 12.horas
  • 28. Referencias groovy.codehaus.org grails.org.mx - @grailsmx http://delicious.com/neodevelop/groovy Programming Groovy - Venkat S.
  • 29. ¡Gracias! Q &A @neodevelop