As described in Wikipedia, the command pattern ..
is a behavioral design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event at a later time.
In a nutshell, the Kommand library implements the command pattern in Kotlin, using Kotlin's coroutines to execute suspending command requests.
- decoupling of the invoker of a request from a particular request
- configuring an object that invokes a request with a request
- defines a separate command object that encapsulate a request
- invoker class delegates a request to a command object instead of implementing a particular request directly
This allows us to have a class that is not coupled to a particular request and is independent of how the request is carried out. The execution of a command, on the other hand, is done in a suspending function that may return immediately or suspend until the request is completes once it is triggered.
interface Command {
suspend fun execute(): Any
suspend fun undo(): Any
interface Invoker {
fun undoCount(): Int
fun redoCount(): Int
fun undoPeek(): Command
fun redoPeek(): Command
suspend fun execute(cmd: Command): Any
suspend fun undo(): Any
suspend fun redo(): Any
suspend fun cancel(): Any
}
}
private val cmdInvoker = CommandInvokerImpl()
...
showBusy(true)
cmdInvoker.execute(ChangeBackgroundColor(vg_root, newColor = getRandomColor(), oldColor = vg_root.backgroundColor))
showBusy(false)
...
showBusy(true)
try {
cmdInvoker.undo()
} catch (e: NothingToUndoException) {
Toast.makeText(this, "Nothing to undo", Toast.LENGTH_SHORT).show()
}
showBusy(false)
...
class ChangeBackgroundColor(private val view: View,
private val newColor: Int,
private val oldColor: Int) : Command {
override suspend fun execute() {
// Simulate some expensive work by delaying the actual execution of the command
delay((1000..2000).random())
view.backgroundColor = newColor
}
override suspend fun undo() {
// Simulate some expensive work by delaying the actual execution of the command
delay((1000..2000).random())
view.backgroundColor = oldColor
}
}
For a complete demonstration, please refer to the sample app and the unit tests in the library project.
- See the unit tests in the library project
- See the sample app
Please note that the execute()
and undo()
return Any
, according to the Command
interface. Therefore, you can return the output of the request/command back to the caller. However, you would need to type cast from Any
to your known object. Perhaps we can now benefit from (Kotlin contracts)[https://kotlinlang.org/docs/reference/whatsnew13.html] to make a smart cast, but I haven't looked into it yet in depth.
Example:
val result = cmdInvoker.execute(MyCommand(arg1,arg2..))
doSomethingWith(result as? MyOutputObject)
- Documentation