Here, we will talk about undoing commands to change to course of history......
The Command Pattern, as described in one of the earlier blogs, has a wide variety of applications, one of which is the most famous text-editing command - Undo.
Usually, to implement Undo mechanisms, the Memento Pattern is used but here's the thing -
- Memento stores the history objects as they are without any manipulation or processing or abstraction. So, once the size of history objects starts to become large (eg : video, images) , the space and time complexities of this pattern become hugely inefficent.
- Which is why, in most modern applications, Command Pattern is used to implement undo because what Command Pattern stores as history object is just some logic that helps restore the actual history object when the Undo command is executed.
This logic is pretty lightweight so space and time complexities are both optimized.
To know more about the fundamentals about the Memento and the Command Pattern, be sure to check out these blogs by me on the same !
At its core, the command pattern consists of 4 interacting bodies
|Interacts with the interface to call a specific function on the receiver side|
|It's the interface establishes and controls communication between the sender and the receiver. It declares an execute( ) method|
|Classes that expose methods that hold the actual implementation logic of a receiver service (eg : AddService, AuthService etc.)|
|A child class of the interface that defines the execute( ) method and which receiver service it shall call.|
The source code for all prominent design patterns has been properly implemented and documented in this GitHub repostitory by me.
Here, in this article, we will be building a simple application that implements the Undo Mechanism the way most modern applications like Google Suite, Adobe Creative Cloud and many more services use, using the Command Pattern method.
You will understand how this abstraction not only makes code more scalable, but also more optimized in terms of space and time
Implementing the Command (Interface)
The top level parent interface that establishes and manages communication between the sender and receiver
- On some request from sender, it creates a Command object
- Also, it declares the execute( ) method which shall be defined on the receiver end to run the logic that answers the sender's request.
Implementing the UndoableCommand class
This is just a child interface of the Command class that simply extends the functionality of the prototype any classes extending this class (i.e. classes having undo feature on them) must have.
Tracking the history of commands
A simple class that is just tracking the history of the current Command object, with respect to which commands were run on it, so we can undo commands easily.
Implementing the Receiver - BoldCommand
These is a simple class that exposes methods holding the implementation logic to change history of the current command object based on a do or and undo
||Method for implementing 2 things - (1) Making modifications to the 'content' string of the current object (2) Pushing the newly modified command object to the current object's history (3) Tracking the previous state of the 'content' string|
||Method for implementing the authentication of an existing user|
Implementing the ConcreteCommand - UndoCommand
A child class of the Command interface.
It does 2 things :
- It keeps track of the History of commands and changes it if an UndoableCommand's execute( ) method is run
Implementing the Invoker
A simple class that gives out an object that developers using this pattern use to execute some receiver function.
- Here, we simply create a HTMLDoc class that manages the state of a simple string ('con) - whether it is in bold or not.
- makeBold( ) - Command that calls makes the string content bold.
- On activating this makeBold( ) function object (instance of our HTMLDoc class), a Command object is created and the execute( ) method is called on it
Using this pattern in our application
Import all the necessary classes and modules into main.java for a final showdown of our application ! Here, I'll be show the adding the !
var document = new HtmlDoc(); var historyOfCmds = new com.ankit.command.undo.History(); var boldCommand = new BoldCommand(document,historyOfCmds); document.setContent("Namaskar !"); System.out.println(document.getContent()); boldCommand.execute(); System.out.println(document.getContent()); var undoCommand = new UndoCommand(historyOfCmds); undoCommand.execute(); System.out.println(document.getContent());
Congrats on unlocked the secret of how most modern apps reverse your actions. You should feel pretty comfortable now developing the your own simple apps like text editors, with undo functionalities in a that contain the right balance of abstraction and control. Until next time...
I shall also be writing some content on using the Command Pattern to implement Composite Commands and Undo, Redo functions so do check them out !!
Whatever I have learnt and implemented here in code as well as the explanation is all from the teachings of the brilliant Mosh Hamedani and his course on Design Patterns which I would recommend you to take if you are really interested in exploring the beauty of Object Oriented Programming