โ† 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"