The Command Design Pattern in Kotlin
The Command Design Pattern is a behavioural design pattern, and as such is responsible for making two actors communicate in an efficient and maintainable way.
Problem
Often, an object (the Sender) makes a request to another object (the Receiver). Doing this in a naive way requires the Sender to know who the Receiver is at compilation time; if the Receiver changes, you will also have to change the Sender’s code, replacing the old Receiver with the new Receiver.
To avoid this, the Sender’s request is encapsulated in a separate object: the Command.
In addition, by transforming the requests into objects, there is the possibility of inserting them into data structures like Queue and then scheduling the execution at a later time.
Main Scenarios:
- Macro
- Undo
- Parallel processing
- GUI buttons
Solution
First to all, let’s see the UML Diagram about the Command Pattern.
- The Invoker is the class who wants execute the task.
- The Command is the interface of a generic operation.
- The Command Implementation is the specific operation: it knows who is the real receiver.
- The Receiver is where the task will be executed.
The Command interface provides two methods and one attribute:
1) execute() will do the task
2) unexecute() will undo the task, useless if isReversible is false
3) isReversible attribute is set in the Command Implementation’s constructor
Not every command needs to be reversible.
Finally, let’s see how it looks like in Kotlin.
The Command interface
interface Command {
val isReversible: Boolean
fun execute()
fun unexecute()
}
The Sender
TheController is the Receiver, the class who makes the task.
// ⬇️ ⬇️ ⬇️ ⬇️
val command: Command = MyCommand(isReversible = true)
// ⬆️ ⬆️ ⬆️ ⬆️
naiveButton.setOnClickListener { //don't use this
TheController.makeStuff()
}
naiveUndo.setOnClickListener { //don't use this
TheController.undoStuff()
}
/** The buttons DON'T KNOW who the Receiver is ! **/
correctButton.setOnClickListener {
command.execute()
}
correctUndo.setOnClickListener {
command.unexecute()
}
The Command implementation
class StuffCommand(override var isReversible: Boolean) : Command {
override fun execute() {
TheController.makeStuff()
}
override fun unexecute() {
if(!isReversible)
return
TheController.undoStuff()
}
}
Final considerations
This pattern can improve the internal quality of your app, try it and write me what do you think about via Telegram!
Github project (to copy 😉, don’t worry I will not snitch you)
Thank you for your time,
Gennaro Daniele Acciaro