Your SlideShare is downloading. ×
  • Like
Creación de Builders y DSL's con Groovy
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Now you can save presentations on your phone or tablet

Available for both IPhone and Android

Text the download link to your phone

Standard text messaging rates apply

Creación de Builders y DSL's con Groovy

  • 554 views
Published

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

Published 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
554
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