โ Back to Design Patterns
๐
Behavioral
Command
Encapsulates a request as an object, enabling undo/redo, queuing, and logging of operations.
The Problem
You need to issue requests without knowing what operation is performed, who performs it, or when. You also want to support undo/redo.
How It Works
- โบRequest is wrapped as an object with execute() and undo() methods
- โบDecouples the invoker (who sends the command) from the receiver (who handles it)
- โบCommands can be queued, logged, or replayed
- โบundo() reverses the effect of execute() โ enables full history management
- โบMacro commands compose multiple commands into one
When to Use
โUndo/redo functionality (text editors, drawing apps)
โTransaction-based systems where rollback is needed
โQueuing or scheduling actions for deferred execution
โLogging and auditing all operations performed
Real-World Examples
๐
iOS
NSUndoManager, UIAction, SwiftUI .onTapGesture closures
๐ค
Android
Room database transactions, WorkManager Tasks, MenuItem.setOnMenuItemClickListener
๐
Web
Redux actions & reducers, CQRS command objects, job queues (Bull, Celery)
Code Examples
Swift
protocol Command {
func execute()
func undo()
}
class TextEditor {
private(set) var content = ""
private var history = [Command]()
func run(_ cmd: Command) { cmd.execute(); history.append(cmd) }
func undoLast() { history.popLast()?.undo() }
func appendCommand(_ text: String) -> Command {
AppendCommand(editor: self, text: text)
}
fileprivate func append(_ t: String) { content += t }
fileprivate func removeLast(_ n: Int) { content = String(content.dropLast(n)) }
}
class AppendCommand: Command {
private let editor: TextEditor
private let text: String
init(editor: TextEditor, text: String) { self.editor = editor; self.text = text }
func execute() { editor.append(text) }
func undo() { editor.removeLast(text.count) }
}
let ed = TextEditor()
ed.run(ed.appendCommand("Hello"))
ed.run(ed.appendCommand(" World"))
print(ed.content) // "Hello World"
ed.undoLast()
print(ed.content) // "Hello"