DSL en Groovy : Builders

Groovy ofrece varias opciones para crear tu propio DSL, siendo una de ellas, y el tema de este artículo, los Builders ("Constructores").

Los builders permiten construir estructuras jerárquicas de manera natural, eliminando la complejidad de que el desarrollador administre la creación de la estructura al mismo tiempo que el código de negocio.

Groovy ofrece 3 opciones para que puedas crear tus propios builders:

  • extendiendo GroovyObjectSupport, es tu responsabilidad alambrar la lógica al sobreescribir invokeMethod/methodMissing principalmente
  • extendiendo BuilderSupport, la manera estandar de muchos builders que existen en la distribución de Groovy
  • extendiendo FactoryBuilderSupport, la manera mas moderna, aumenta los mecanismos provistos por BuilderSupport

Supongamos que necesitamos una clase que obtenga un SQL desde un archivo properties y que nos regrese una cadena con dicho SQL

Primero, debemos definir nuestro builder:

 

Finalmente, lo usamos desde cualquier clase o script groovy:

 

Ahora, hagamos unos cambios para permitir la ejecucion de los statements en el mismo builder (esto solo es para ejemplificar a los Builders y no pretende ser una propuesta de solucion en la vida real):

 

Finalmente, nuestra llamada al builder sera asi :

 

y nuestro archivo statements es un properties de la siguiente forma :

 

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

Dices que hay tres opciones

Dices que hay tres opciones para crear un builder:

  • extendiendo GroovyObjectSupport
  • extendiendo BuilderSupport
  • extendiendo FactoryBuilderSupport

Pero en tu código no usas ninguno de los tres.

Luego mencionas que es un DSL ( Lenguaje de Dominio Específico ) pero el código que se muestra es Groovy mismo. No parece ser un lenguaje diferente.

Eso sí, esta muy interesante las cosas que mencionas, como el "methodMissing", que seguramente fue tomado de Ruby ( el lenguaje que el creador de Groovy quería emular ) que a su vez lo tomó de Smalltalk y se ejecuta cuando se llama a un método que la clase actual no contempla.

Imagen de ezamudio

es la primera

Oscar, extender GroovyObjectSupport no es poner "extends bla" en la definicion de la clase... como dice el post, hay que sobreescribir invokeMethod y/o methodMissing, y es lo que hace el DBBuilder.

Los DSL's en Groovy no dejan de ser llamadas a Groovy, lo que se hace es que se puede construir una sintaxis dinamica para interpretar un buen de cosas, aprovechando cosas de Groovy como setters y getters tipo propiedades (asignacion, etc), o que te puedes saltar los parentesis en algunas llamadas y que el salto de linea es un terminador de sentencia, y cosas como el "with" de Groovy. Esto es un ejemplo simple, pero imaginate si consideras un tercer argumento que fuera un closure que le pasas al each del resultado de sql.rows, entonces podrias hacer algo asi:

 

En Gradle por ejemplo cuando haces algo como esto:

 

todo eso termina siendo codigo que se invoca sobre un objeto tipo Project, es como si hicieras:

 

Imagen de maleficarum

No puse detalles

Oscar, tienes razon y no aclare: este es el primer ejemplo de varios que pondre sobre DSL, que incluye algo mas de builders, uso de metaclass, etc. Pero como dice Enrique, parece ser groovy todo, por que que de hecho es groovy. Al final, la intencion es ejemplificar como haces para que los desarrolladores usen "tus modulos, plugins o api's" de una manera mas "coherente"; en este ejemplo, buscas los statements como una llamada a algun metodo (que no existe), pero que tu "api" sabe que debe buscar como prefijo y no como funcion. Finalmente, para el usuario es mas "intuitivo" ejecutar :

 

que

 

y como comenta tambien Enrique, podriamos modificarlo para pasarle un closure que haga algo con los resultados; entonces podrias ejecutar todo lo que tu quieras despues de obtener los resultados, que al final, lo importante y la razon de cada API es que no tengas que estar lidiando con lo de siempre, y solo enfocarte a la logica del negocio.

Gracias por aclarar lo 3 puntos (que especificare en el proximo post).

Si, ya veo, ahora tiene

Si, ya veo, ahora tiene sentido.

:)

Imagen de bferro

¿Un DSL para el dominio de la solución?

Creo que Oscar tiene razón cuando dice que no ve un DSL en los ejemplos.
Un DSL trata de buscar en la sintaxis del programa un vocabulario común con los usuarios del dominio y los implementadores de la solución, de ahí el término "lenguaje específico del dominio". En los ejemplos , tanto los usuarios como los implementadores conocen el vocabulario del lenguaje de programación y por supuesto que no hay mejor DSL para el dominio de la solución que el propio lenguaje de programación, aprovechando cosas como las que en este post se están comentando.

No basta tener un vocabulario común para que un DSL tenga éxito; es necesario que además de tener una correspondencia directa de los nombres del dominio en el espacio de la solución, también se pueda usar el mismo lenguaje para describir las interacciones y colaboraciones en el dominio del problema. Groovy, Ruby y Scala son buenos para eso.