1. Overview

The Command Design Pattern is a software design pattern that allows the encapsulation of a request as an object. This enables the client to parameterize and execute the request at a later time. This pattern is useful when building applications where the client needs to queue, log, or undo operations.

In this tutorial, we’ll explore how to implement the Command Design Pattern in Kotlin.

2. Command Design Pattern Components

Essentially, the Command Design Pattern is a way to separate the requester of an action (the client) from the object that performs the action (the receiver). By encapsulating the request as an object, we can pass it around and manipulate it as needed. This allows us to parameterize the request with different arguments, queue it up for later execution, or even undo it if needed.

There are several components to the Command Design Pattern:

  • Command interface: This is an interface that defines the common methods for all commands. It usually has a single method called execute() that performs the action.
  • Command class: This is a class that implements the Command interface and defines the behavior of a specific command. It contains a reference to the object that will perform the action.
  • Invoker: This is a class that requests an action to be performed by a command. It doesn’t know how the action will be performed or who will perform it.
  • Receiver: This is a class that performs the actual action. The command object sends the request to the receiver object, which then performs the action.

3. Implementing the Command Design Pattern

To implement the Command Design Pattern in Kotlin, we’ll create a simple example that demonstrates the various components of the pattern. In this example, we’ll create a text editor that supports basic editing operations like cut, copy, and paste.

3.1. The Command Interface

First, we’ll create the Command interface, which defines the execute() method that all commands must implement:

interface Command {
    fun execute()
    fun undo()
}

3.2. The Command Class

Next, we’ll create the Command classes, which implement the Command interface and defines the behavior of specific commands.

In this example, we’ll create three concrete commands: CutCommand, CopyCommand, and PasteCommand:

class CutCommand(private val receiver: TextEditor, private val clipboard: Clipboard) : Command {
    override fun execute() {
        clipboard.content = receiver.cut()
    }
    
    override fun undo() {
        receiver.write(clipboard.content)
        clipboard.content = ""
    }
}
class CopyCommand(private val receiver: TextEditor, private val clipboard: Clipboard) : Command {
    override fun execute() {
        clipboard.content = receiver.copy()
    }
    
    override fun undo() {
        clipboard.content = ""
    }
}
class PasteCommand(private val receiver: TextEditor, private val clipboard: Clipboard) : Command {
    override fun execute() {
        receiver.write(clipboard.content)
    }
    
    override fun undo() {
        receiver.delete(clipboard.content)
    }
}

Each command class takes a TextEditor object as a command receiver in its constructor as well as a Clipboard object which will help us to implement editing functionality. When the execute() method is called, the receiver performs the actual editing operation.

3.3. The Invoker Class

Next, we’ll create the Invoker class, which requests an action to be performed by a command:

class TextEditorInvoker {
    private val commands = mutableListOf<Command>()

    fun executeCommand(command: Command) {
        commands.add(command)
        command.execute()
    }

    fun undo() {
        if (commands.isNotEmpty()) {
            commands.removeLast().undo()
        }
    }
}

The TextEditorInvoker class contains a list of commands that have been executed, and it provides methods for executing a command and undoing the last executed command.

The executeCommand() method adds the command to the list and calls its execute() method, while the undo() method removes the last command from the list and calls its undo() method.

3.4. The Receiver Class

Finally, we’ll create the Receiver class, which performs the actual editing operation. In this example, we’ll create the TextEditor class:

class TextEditor(initialContent: String) {
    private var content = initialContent

    fun cut(): String {
        val cutContent = content.takeLast(1)
        content = content.dropLast(1)
        return cutContent
    }

    fun copy(): String {
        return String(content)
    }

    fun write(text: String) {
        content += text
    }

    fun delete(text: String) {
        content = content.removeSuffix(text)
    }

    fun print() {
        println(content)
    }
}

The TextEditor class has a content property that stores the edited text, and it offers methods to cut, copy, write, and delete text. The cut() method removes the last character of the text, the copy() method returns a copy of the content, the write() method appends provided text to the end of the text, and the delete() method removes provided text from the end of the text.

We’ll also create a Clipboard class for this demonstration to implement the functionality of commands:

data class Clipboard(var content: String = "")

For simplicity, it stores only the last cut or copied text in a variable.

3.5. Putting It All Together

With all the components in place, we can now create a simple example. Here, we’ll demonstrate how to use the Command Design Pattern to implement basic editing operations in a text editor:

val clipboard = Clipboard()
val editor = TextEditor("Baeldung")
val invoker = TextEditorInvoker()

invoker.executeCommand(CutCommand(editor, clipboard)) // cuts last character of initial content to clipboard
invoker.executeCommand(CopyCommand(editor, clipboard)) // copies current content to clipboard
invoker.executeCommand(PasteCommand(editor, clipboard)) // pastes current clipboard content to text editor

editor.print() // prints "BaeldunBaeldun"

invoker.undo() // undoes the paste command

editor.print() // prints "Baeldun"

Here, we create a Clipboard, a TextEditor, and TextEditorInvoker objects. We then execute a series of commands (cut, copy, and paste) using the executeCommand() method of the TextEditorInvoker object. Finally, we print the resulting text, undo the last command, and print the resulting text again.

4. Conclusion

In software development, the Command Design Pattern is often used in conjunction with other patterns, such as the Factory Method pattern and the Observer pattern. It can be particularly useful in applications where users need to undo or redo actions, such as text editors or drawing programs. By encapsulating each action as a command object, we can easily keep track of what has been done and allow the user to undo or redo actions as needed.

In this article, we’ve learned how to implement the Command Design Pattern in Kotlin using a simple example of a text editor that supports basic editing operations. By using this pattern, we can make our code more flexible and easier to maintain, which can lead to better software design and development.