• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Creación de Builders y DSL's con Groovy
 

Creación de Builders y DSL's con Groovy

on

  • 912 views

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

Statistics

Views

Total Views
912
Views on SlideShare
912
Embed Views
0

Actions

Likes
1
Downloads
13
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />

Creación de Builders y DSL's con Groovy Creación de Builders y DSL's con Groovy Presentation Transcript

  • Creación de builders y DSL's con Groovy @neodevelop - @synergyj
  • Agenda Groovy Metaprogramación Builders DSL
  • {}
  • {}
  • 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
  • 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
  • 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
  • Meta-object Protocol GroovyObject En Groovy se trabaja con 3 tipos de objetos: POJOs POGOs Groovy Interceptors
  • 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); }
  • 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(...); }
  • 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")
  • 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 }
  • 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 }
  • 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()
  • 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
  • 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()
  • 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}"]) }
  • ¿Qué es un builder?
  • 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
  • Metaprogramación Builders BuilderSupport
  • //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 }
  • 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" } } }
  • Lenguajes de dominio específico
  • 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
  • ¿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
  • tomar 4.pastillas, de: lsd, en: 12.horas
  • 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
  • Referencias groovy.codehaus.org grails.org.mx - @grailsmx http://delicious.com/neodevelop/groovy Programming Groovy - Venkat S.
  • ¡Gracias! Q &A @neodevelop