8. 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
9. 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
10. 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
14. 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")
15. 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
}
16. 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
}
22. 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
24. //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
}
25. 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"
}
}
}
27. 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
28. ¿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