En la nota anterior vimos algunos conceptos básicos de Drools e hicimos un pequeño ejemplo utilizando un archivo de reglas DRL. En este segundo artículo vamos a modificar el mismo ejemplo para crear un DSL (lenguaje específico de dominio), el cuál sea más cecano al lenguaje del usuario.
Para crear el DSL en Drools vamos a escribir un archivo con la extensión dsl, en el cuál vamos a poner las condiciones y las consecuencias que existirán en nuestro lenguaje específico de dominio. En este archivo vamos a mapear las construcciones sintácticas que formarán nuestro DSL (oraciones en lenguaje coloquial, que utilizará el usuario en este caso) con la resolución en instrucciones en lenguaje DRL (y en este caso en dialecto java) que pueda entender Drools.
Luego de ésto tenemos que crear un archivo con las reglas que definiremos y utilizará la aplicación, en este caso expresadas en el lenguaje natural del DSL.
Manos a la obra
El archivo .dsl con la especificación del DSL es el siguiente:
PoliticaRrhh_Ej2.drl:
[condition][]El promedio de conocimientos esta entre {promedioMinimo} y {promedioMaximo} =empleado : Empleado(promedioConocimientos >= {promedioMinimo} && <= {promedioMaximo}) [consequence][]Ascender a {cargo}=empleado.setCargo({cargo}); [consequence][]El salario es de {salario} pesos =empleado.setSalario(BigDecimal.valueOf({salario})); [consequence][]Imprimir {cargo}: nombre =System.out.println({cargo} + ": " + empleado.getNombre());
Como se puede ver en este ejemplo definimos las condiciones (lo que va a ir en el when) y las consecuencias (lo que va en el then) a través de las palabras reservadas encorchetadas condition y consequence.
Luego las líneas se dividen en dos secciones separadas por un "=". Lo que está antes del igual es la sintáxis de nuestro DSL (las oraciones en el lenguaje del dominio/usuario), y lo que está después son las instrucciones que damos a Drools en su lenguaje DRL y Java (es decir el código que se ejecutará).
Las variables o argumentos que necesitemos usar se declaran entre llaves, como por ejemplo {cargo}. Éstas variables las toma del archivo de reglas (.drl) por su posición y las aplica a las sentencias del otro lado del igual por el nombre que le definimos antes del mismo.
Por ejemplo, en la línea:
[condition][]El promedio de conocimientos esta entre {promedioMinimo} y {promedioMaximo} =empleado : Empleado(promedioConocimientos >= {promedioMinimo} && <= {promedioMaximo}
Estamos definiendo la condición de aplicación de la regla en un lenguaje más natural para el usuario. Por esto mismo tema la regla se autoexplica y la puede entender cualquiera (y esto es una de las ventajas del DSL!!!).
Aquí definimos 2 variables, {promedioMinimo} y {promedioMaximo}, que tomarán los valores que escriba en el archivo de reglas (.drl) en esa posición, y son utilizadas por los mismos nombres que acabamos de definir en la sentencia DRL después del igual.
Por si no lo mencioné antes, DRL es el lenguaje de Drools en el cuál se escriben las condiciones. Es lo mismo que utilizamos en el ejemplo de la primer nota en el archivo PoliticaRrhh_Ej1.drl, y estamos creando las mismas reglas.
En este caso simplificamos un poco la condición del and, nombrando una sola vez el atributo promedioConocimientos y uniendo por un &&, sólo para que sea más reducida la línea y para mostrar algo más del lenguaje DRL de Drools.
Con esto ya tenemos listo y explicado nuestro archivo dsl. Podríamos decir que lo que hacemos aquí es un metalenguaje que define el lenguaje que usaremos en las reglas, en el próximo archivo.
El archivo de reglas
Ya tenemos definido nuestro DSL, pero no tenemos ninguna regla todavía. Si se fijan no hay ningún valor definido para los promedios de conocimiento ni se menciona ningún cargo o salario en particular.
Lo que hicimos es crear las herramientas del lenguaje que nos permitiran combinarlas y crear reglas.
Para crear las reglas expresándolas en el DSL que acabamos de definir necesitamos crear un archivo de reglas (.drl). Éste archivo es el siguiente:
PoliticaRrhh_Ej2.dsl:
package com.dosideas.drools.ejemplo2.rrhh; import com.dosideas.drools.ejemplo2.rrhh.Empleado; import java.math.BigDecimal; expander PoliticaRrhh_Ej2.dsl rule "Ascender a Programador" when El promedio de conocimientos esta entre 8 y 10 then Ascender a "Programador" Imprimir "Programador": nombre El salario es de 1000 pesos end rule "Ascender a Lider de Proyecto" when El promedio de conocimientos esta entre 4 y 7 then Ascender a "Lider de Proyecto" Imprimir "Lider de Proyecto": nombre El salario es de 2000 pesos end rule "Ascender a Gerente" when El promedio de conocimientos esta entre 0 y 3 then Ascender a "Gerente" Imprimir "Gerente": nombre El salario es de 3000 pesos end
Como se ve en este archivo estamos creando las mismas reglas que en el artículo anterior, pero esta vez expresadas de una forma más "humana".
Aquí utilizamos las construcciones sintácticas que definimos en el archivo dsl, y creamos distintas reglas y asignamos valores a las variables que definimos en el dsl.
El usuario sólo tiene que ver (el crea) este archivo. Para esto obviamente debe conocer el significado de cada frase del DSL, previo acuerdo con los desarrolladores. El dsl se puede decir que es la instrumentación en Drools de éstas reglas.
Como en el ejemplo de la primer nota, en la primer línea le damos un nombre al paquete de reglas. Luego ponemos los imports que debemos utilizar.
A diferencia de ese archivo, lo que sigue es la instrucción expander que le indica a Drools que vamos a utilizar ese dsl específico.
Por último definimos las 3 reglas, dónde usamos distintos valores para que tome distintos objetos (hechos) de la memoria de trabajo de Drools.
Con las palabras reservadas when y then separamos las condiciones de las consecuencias, como ya hemos visto.
Como puede verse, en este archivo drl se asignan valores a las variables del dsl, como los conocimientos, los cargos y los salarios.
Esta parte de los valores de las variables es la parte variable (valga la redundancia) de las frases de las reglas (lo que se puede cambiar). Al parecer Drools machea los strings (perdón Cervantes) de las líneas del drl con las definidas en el dsl para identificar las instrucciones a ejecutar.
Evidentemente también podemos cambiar las acciones y consecuencias de las reglas, agregando, quitando o mezclando las sentencias definidas en el dsl, y crear nuevas reglas o borrar otras.
Los cambios en el código Java
Lo único que nos queda por ver son los cambios necesarios en el código java para que lea las nuevas reglas basadas en un archivo dsl.
Aquí, lo único que vamos a cambiar es una parte del método leerReglas() de la clase PoliticaRrhhBo.
PoliticaRrhhBo.leerReglas():
private static RuleBase leerReglas() throws Exception { //Leemos el archivo de reglas (DRL) Reader source = new InputStreamReader( PoliticaRrhhBo.class.getResourceAsStream("PoliticaRrhh_Ej2.drl")); //Leemos el DSL Reader dsl = new InputStreamReader( PoliticaRrhhBo.class.getResourceAsStream("PoliticaRrhh_Ej2.dsl")); //Construimos un paquete de reglas PackageBuilder builder = new PackageBuilder(); //Parseamos y compilamos las reglas en un único paso builder.addPackageFromDrl(source, dsl); // Verificamos el builder para ver si hubo errores if (builder.hasErrors()) { System.out.println(builder.getErrors().toString()); throw new RuntimeException( "No se pudo compilar el archivo de reglas."); } //Obtenemos el package de reglas compilado Package pkg = builder.getPackage(); //Agregamos el paquete a la base de reglas //(desplegamos el paquete de reglas). RuleBase ruleBase = RuleBaseFactory.newRuleBase(); ruleBase.addPackage(pkg); return ruleBase; }
Aquí sólo agregamos la segunda línea, la que lee el archivo dsl:
//Leemos el DSL Reader dsl = new InputStreamReader( PoliticaRrhhBo.class.getResourceAsStream("PoliticaRrhh_Ej2.dsl"));
Y modificamos la línea donde parseamos y compilamos las reglas, agregandole al PackageBuilder un paquete en base al dsl y drl llamando a la correspondiente versión sobrecargada del método:
//Parseamos y compilamos las reglas en un único paso builder.addPackageFromDrl(source, dsl);
Ah, y cambiamos el nombr del archivo drl que leemos en la primer línea, ya que le puse otro nombre a éste:
//Leemos el archivo de reglas (DRL) Reader source = new InputStreamReader( PoliticaRrhhBo.class.getResourceAsStream("PoliticaRrhh_Ej2.drl"));
El resto de la clase, como la clase Empleado siguen igual.
La salida del programa y el final
El resultado del programa es el siguinete:
Gerente: Pedro Lider de Proyecto: Jose Programador: Juan Empleado: Juan promedio=9 cargo=Programador salario=1000 Empleado: Jose promedio=6 cargo=Lider de Proyecto salario=2000 Empleado: Pedro promedio=2 cargo=Gerente salario=3000
Como vemos éste es el resultado esperado y sigue siendo la misma salida que en el ejemplo anterior.
Lo que hicimos hasta aquí fué definir las mismas reglas de negocio que en la nota y ejemplo anterior, pero ésta vez de una forma más cercana al usuario a través de la definición de un dsl.
Ésta es una opción muy interesante, que nos permite entendernos con los usuarios y dejar que ellos administren las reglas del negocio en su propio lenguaje.
Queda una opción más para especificar reglas, que es a través de una planilla de cálculo. Cómo la nota se hizo larga veremos esta opción en una próxima entrega.
Descargar el proyecto de ejemplo
Pueden descargar un proyecto de ejemplo con este ejemplo y todas las librerías necesarias para ejecutarlo.