StudyDSA logoStudyDSA

Command Palette

Search for a command to run...

Sign InSign Up
Sign Up

Data StructuresAlgorithmsBig-O NotationDesign PatternsSystem DesignMachine LearningPhysicsRoboticsAI Research

Factory MethodAbstract FactoryBuilderPrototypeSingletonAdapterBridgeCompositeDecoratorFacadeFlyweightProxyChain of ResponsibilityCommandIteratorMediatorMementoObserverStateStrategyTemplate MethodVisitor

Definition
StudyDSA

Where complexity meets clarity.
By Armas Zarra.

Topics

  • Data Structures
  • Algorithms
  • Big-O Notation
  • Robotics
  • AI Research
  • Machine Learning

Practice

  • Blind 75
  • LeetCode 75
  • NeetCode 150

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Armas Films LLC

Design PatternsCommand

Command

A behavioral pattern that freezes a request into an object carrying its receiver, arguments, and reverse gear, which is what makes undo, queues, and logs possible.

Definition

The user presses Ctrl+Z, and your code has nothing to work with: the insert that needs reversing was a direct method call that finished and vanished. Command is the Gang of Four behavioral pattern that turns a request into an object carrying everything needed to perform it, and usually to reverse it, which is how the catalog unlocks queuing, logging, and undoable operations from one move.


A command is really just a function call frozen solid: the receiver, the method, and the arguments packed into an object that can be executed later, executed elsewhere, or executed in reverse. Every undo stack you've ever pressed Ctrl+Z into is a pile of these objects waiting to run backward.

The Call That Left No Trace

Here's the hardwired version. A toolbar button and a keyboard shortcut both insert text by calling the document directly, which works perfectly right up until the feature request that defines text editors arrives:

The problem is ontological: there is no thing in this program that represents "the insert that just happened". The call ran, the stack frame popped, and the only evidence is the changed text, which doesn't know what changed it. Undo, redo, macros, an operation log, and a job queue all need the same missing ingredient, an object that outlives the call it describes.

Freeze the Call, Keep the Receipt

The command version builds that object. InsertCommand packs the document and the text together with an execute() that inserts and an undo() that removes exactly what was inserted. A History invoker runs commands and stacks them. The example inserts hello, then world, then undoes once. Before reading past the code, predict the final printed text and what the history stack still holds:

The document reads hello and the stack holds one command. The undo popped the most recent receipt, InsertCommand(doc, " world"), and asked it to reverse itself, which it could do because it still remembered its own text and could compute remove_last(6). The first insert's receipt remains on the stack, one more Ctrl+Z away from an empty document. Nothing about the document class changed to make any of this possible.

Who Does What

Five roles, more than most patterns, each small. The Command interface declares execute() and here undo(). ConcreteCommands (InsertCommand) bind a receiver to one action with its arguments. The Receiver (Document) does the actual work and knows nothing about commands. The Invoker (History) runs commands and does the bookkeeping without ever looking inside one. The Client wires it together, deciding which commands exist and who invokes them.


The pattern works like a restaurant order ticket. The waiter doesn't cook your meal, they write a ticket that captures the request and hand it to the kitchen, and the ticket itself can sit in a queue, be handed between staff, and survive as a record after the food is served. The analogy stops exactly where the pattern's honest limit is: a meal cannot be un-cooked. Commands only undo when a true inverse exists, and operations like sending an email or charging a card have no undo() worth the name, only compensating actions that are new commands in their own right.

Command: Step-by-Step

Installing the pattern is four moves:

1. Pack the Call Into an Object

InsertCommand carries the receiver and the arguments inside itself, so execute() needs nothing from outside. A frozen call can wait in a queue, ride over a wire, or sit in history, none of which a finished method call can do.

2. Pair Every execute() With Its Inverse

undo() is the half that earns the pattern its keep: insert pairs with remove, add pairs with subtract. Writing them side by side in one class keeps the pairing honest when either half changes.

3. Let the Invoker Keep the Receipts

History.run() executes and then files the command on a stack. The invoker never looks inside what it runs, which is why one history class serves every command the app will ever grow.

4. Wire Inputs to Commands, Not Methods

The toolbar button, the keyboard shortcut, and the macro recorder all construct the same InsertCommand and hand it to the history. One action, many triggers, zero duplicated calls.
The Two Ways to Undo

Undo systems split into two camps, and this pattern is one of them. Command-based undo stores the inverse operation, which is cheap in memory and demands that every action know how to reverse itself. Snapshot-based undo stores copies of prior state and restores wholesale, costing memory for the simplicity of never computing an inverse, an approach with its own pattern name, Memento. Real editors mix them: cheap reversible edits ride commands, while operations too gnarly to invert checkpoint a snapshot first.


The JDK's Swing toolkit ships the command-style machinery wholesale: UndoManager manages a list of UndoableEdit objects, undoes them in reverse order, and on each new edit discards everything past the current position, which is the clear-the-redo-branch rule you've watched every editor enforce when you undo twice and then type.

When a Closure Is Enough

You might think languages with first-class functions made this pattern obsolete, and for half its uses they did. A closure already freezes a receiver and arguments into a callable value, so a command whose only method is execute() is pure ceremony, one class where () => doc.insert(text) would do. The Go tab leans into this honestly: its command is a struct of two function values, Do and Undo, with closures capturing the receiver, and the GoF class diagram is nowhere in sight.


What closures don't give you is the second verb. The moment an action needs undo(), needs serializing into a job queue, or needs inspecting in a log, the request wants named structure, and an object with two paired methods says things a lambda can't. The Rust tab makes the same call with a trait, passing the receiver into execute(&mut doc) at run time, which keeps ownership clean and makes the receiver dependency visible in the signature.

Best Practices

The pattern is a short interface, and these five decide whether the undo stack can be trusted:


A command with only execute() is a closure wearing a class costume, and the closure was less code. Reach for the object when you need undo(), serialization, or queuing, and reach for a plain function when you don't.

Check Yourself

Two questions before you go. First: after two inserts and one undo, the document read hello. What object did the history pop, and what two facts did it carry that made the reversal possible? Second: your language has closures, so name the requirements that move an action from "use a function" to "use a command object". Both answers are on this page.


Then go spot it in the wild, where this pattern is unusually easy to catch. Every undo stack in every editor is a pile of command objects, every task queue that serializes jobs to workers is freezing calls into transportable objects, and database write-ahead logs replay frozen operations after a crash. Last, open your own app's toolbar handlers and ask of each one: if undo became a requirement tomorrow, does this click leave behind enough evidence to reverse itself? A no is this pattern's front door.

the_hardwired_call.py

class Document:
    def __init__(self):
        self.text = ""

    def insert(self, text: str) -> None:
        self.text += text

doc = Document()

def on_toolbar_click() -> None:
    # The button reaches straight into the document
    doc.insert("hello")

def on_keyboard_shortcut() -> None:
    # The shortcut duplicates the same hardwired call
    doc.insert("hello")

# Example usage
on_toolbar_click()
print(doc.text)  # hello
# Now the user presses Ctrl+Z. Undo what, exactly?
# The call is finished and left no trace of itself.

command.py

from abc import ABC, abstractmethod

class Document:  # Receiver: knows how to do the actual work
    def __init__(self):
        self.text = ""

    def insert(self, text: str) -> None:
        self.text += text

    def remove_last(self, count: int) -> None:
        self.text = self.text[:-count]

class Command(ABC):  # Command: a frozen call with a reverse gear
    @abstractmethod
    def execute(self) -> None: ...

    @abstractmethod
    def undo(self) -> None: ...

class InsertCommand(Command):  # ConcreteCommand
    def __init__(self, doc: Document, text: str):
        self.doc = doc      # the receiver travels inside
        self.text = text    # so do the arguments

    def execute(self) -> None:
        self.doc.insert(self.text)

    def undo(self) -> None:
        self.doc.remove_last(len(self.text))

class History:  # Invoker: runs commands and keeps the receipts
    def __init__(self):
        self._done: list[Command] = []

    def run(self, command: Command) -> None:
        command.execute()
        self._done.append(command)

    def undo(self) -> None:
        if self._done:
            self._done.pop().undo()

# Example usage
doc = Document()
history = History()
history.run(InsertCommand(doc, "hello"))
history.run(InsertCommand(doc, " world"))
print(doc.text)  # hello world

history.undo()
print(doc.text)  # hello