The main idea behind this article about building a DSL is to play and experiment with some design patterns on a surface level.
We’ll have a character with a position, and we’ll move it in four directions thanks to the commands grouped in a DSL. These commands can be undone and move the character back to a previous position.
Building a DSL: Character
The first thing that we’ll create is our Character domain class, where we’ll put all the logic related to the character’s movement. It will be able to move up, down and sideways.
Memento is a pattern design with the goal of stacking an object’s state at a given time.
We’ll create our Memento class to stack each of our Character’s previous states.
Originator creates a Memento object containing a photography of its internal state.
We’ll create an Originator interface and implement it on our Character class so it knows how to create memories and restore itself from one of them.
At this point, I’ve realized that Memento doesn’t seem fit for immutable code. It would be normal to match the values of the current instance with the Memento’s one.
Command is the base class of all the commands. Each command shall implement said interface and its ‘do’ method, which will execute different actions through polymorphism.
- We save a memory of the object we’re about to modify.
- The ‘do’ method will be abstract, and each command will have a different implementation through polymorphism.
- The ‘undo’ method will be in charge of undoing the action done by the command through the memento. We’ll implement it a single time in the base class.
Concrete Command is each of the commands that our system has. They must inherit the Command base class. Each one will have its own implementation.
It’s important that the commands are devoid of any logic aside from calling the class method that contains all logic. In our example, we’ll create a command to move our Character on any direction.
CommandManager is the component that will execute the commands and stack their history with the goal of doing and undoing the commands.
Each of this class’s methods returns a Tupla with:
- A new CommandManager instance with an updated history of commands.
- A new updated Character instance.
Creating the DSL
Extension methods is a pattern that has the capability of aggregating behaviour to already existing classes without modifying the original classes.
We’ll add methods to the Tupla using exension methods, to group all commands and build our DSL.
Using the DSL
The console output will be:
Now all that’s left is adding the commands we’ve created to the DSL to extend it.