La idea de este artículo sobre cómo construir un DSL es jugar y experimentar con algunos patrones de diseño sin profundizar demasiado en ninguno de ellos.

Tendremos un personaje con una posición y mediante comandos agrupados en un DSL, podremos moverlo en 4 direcciones. Los comandos podrán deshacerse y mover al personaje a posiciones anteriores.

Character

Lo primero que vamos a crear es nuestra clase de dominio Character, donde pondremos toda la lógica referente al movimiento del personaje. Podrá moverse hacia arriba, abajo y a ambos lados.

 

DSL character

Memento

Memento, es un patrón de diseño cuya finalidad es almacenar el estado de un objeto en un momento dado.

Crearemos nuestra clase Memento para almacenar cada uno de los estados anteriores de nuestro Character.

DSL memento

Originator

Originator, crea un objeto Memento conteniendo una fotografía de su estado interno.

Crearemos una interface originator y la implementaremos en nuestra clase Character para que sepa crear recuerdos y restaurarse a partir de uno de ellos.

DSL originator

En este punto, me he dado cuenta de que Memento no parece estar pensado para código inmutable. Lo normal sería igualar los valores de la instancia actual con los del Memento.

Command

Command, es la clase base de todos los comandos. Cada comando deberá implementar dicha interfaz y su método do, que mediante polimorfismo ejecutará acciones diferentes.

  • Guardamos un recuerdo del objeto que vamos a modificar.
  • El método ‘do’ será abstracto y mediante polimorfismo cada comando tendrá una implementación distinta.
  • El método undo se encargará de deshacer la acción que ha realizado el comando a través del memento. La implementaremos una única vez en la clase base.

DSL command

Concrete Command

Concrete Command, es cada una de los comandos que tiene nuestro sistema. Deben heredar de la clase base Command. Cada uno tendrá su propia implementación.

Es importante que los comandos no contengan lógica alguna más que llamar al método de la clase que contiene toda la lógica. Para nuestro ejemplo vamos a crear un comando para mover Character en cada dirección.

DSL concrete command

CommandManager

CommandManager, será el componente encargado de ejecutar los comandos y almacenar el histórico de estos, con la finalidad de poder deshacer y rehacer los comandos.

Cada uno de los métodos de esta clase retorna una Tupla con:

  • Una nueva instancia de CommandManager con el histórico de comandos actualizado.
  • Una nueva instancia del Character actualizado.

DSL commandmanager

Creando el DSL

Extension methods, es un patrón que tiene la capacidad de agregar comportamiento a clases ya existentes sin necesidad de modificar las clases originales.

Haciendo uso extension methods, vamos a añadir métodos a la Tupla, para agrupar todas los comandos y construir nuestro DSL.

Usando el DSL

La salida por consola será:

Character(x=-1, y=-1)

Ahora solo nos queda añadir los comandos que creemos al DSL para extenderlo.