Creación de Builders y DSL's con Groovy

879 visualizaciones

Publicado el

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

Publicado en: Tecnología
0 comentarios
1 recomendación
Estadísticas
Notas
  • Sé el primero en comentar

Sin descargas
Visualizaciones
Visualizaciones totales
879
En SlideShare
0
De insertados
0
Número de insertados
2
Acciones
Compartido
0
Descargas
14
Comentarios
0
Recomendaciones
1
Insertados 0
No insertados

No hay notas en la diapositiva.































  • Creación de Builders y DSL's con Groovy

    1. 1. Creación de builders y DSL's con Groovy @neodevelop - @synergyj
    2. 2. Agenda Groovy Metaprogramación Builders DSL
    3. 3. {}
    4. 4. {}
    5. 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. 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. 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. 8. Meta-object Protocol GroovyObject En Groovy se trabaja con 3 tipos de objetos: POJOs POGOs Groovy Interceptors
    9. 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. 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. 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. 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. 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. 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. 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. 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. 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. 18. ¿Qué es un builder?
    19. 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. 20. Metaprogramación Builders BuilderSupport
    21. 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. 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. 23. Lenguajes de dominio específico
    24. 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. 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. 26. tomar 4.pastillas, de: lsd, en: 12.horas
    27. 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. 28. Referencias groovy.codehaus.org grails.org.mx - @grailsmx http://delicious.com/neodevelop/groovy Programming Groovy - Venkat S.
    29. 29. ¡Gracias! Q &A @neodevelop

    ×