Command

Learning Objectives

Represent requests as objects that can be queued, logged and undone.

Implement undo/redo and macros.

Prerequisites: Module 40.

Intuition and Definition

Intent: encapsulate a request as an object, letting you parameterize clients with requests, queue or log them, and support undoable operations.

Participants: Command (execute(), often undo()), concrete commands holding a receiver and parameters, an invoker (button, scheduler, history) that executes commands without knowing what they do.

Why It Matters

Once a request is an object it can be stored, delayed, retried, logged, replayed, combined into macros and reversed.

Syntax and Language Notes

In languages with closures, a command can be a function: queue.push(() => motor.start()). Undo needs a pair of functions or an object with two methods.

Complete Example

#include <iostream>
#include <memory>
#include <string>
#include <vector>

class Seat {                                               // receiver
    int position_ = 5, heat_ = 0;
public:
    void move(int d) { position_ += d; }
    void heat(int level) { heat_ = level; }
    int heatLevel() const { return heat_; }
    std::string state() const { return "position " + std::to_string(position_) + ", heat " + std::to_string(heat_); }
};

struct Command {
    virtual ~Command() = default;
    virtual void execute() = 0;
    virtual void undo() = 0;
    virtual std::string name() const = 0;
};
class MoveSeat : public Command {
    Seat& s_; int d_;
public:
    MoveSeat(Seat& s, int d) : s_(s), d_(d) {}
    void execute() override { s_.move(d_); }
    void undo() override { s_.move(-d_); }
    std::string name() const override { return "move " + std::to_string(d_); }
};
class SetHeat : public Command {
    Seat& s_; int level_, previous_ = 0;                   // remembers what it needs for undo
public:
    SetHeat(Seat& s, int l) : s_(s), level_(l) {}
    void execute() override { previous_ = s_.heatLevel(); s_.heat(level_); }
    void undo() override { s_.heat(previous_); }
    std::st
…
after commands: position 8, heat 2
after undo:     position 8, heat 0
after redo:     position 8, heat 2
after macro:    position 2, heat 1
macro undone:   position 8, heat 2

Step-by-Step Walkthrough

Each command stores its receiver and parameters; SetHeat also stores the previous level so undo is exact.

History executes commands and keeps them; undo reverses the last one and moves it to the redo stack; a new command clears redo (standard editor behaviour).

Macro is a Composite of commands; undoing it undoes its steps in reverse order.

Common Mistakes

Undo that recomputes instead of restoring (e.g. "heat −2"), which breaks when state changed in between; store what you need.

Commands holding references to objects that die before the command runs (queued commands).

Not clearing redo after a new command — redo then applies to a different state.

Trade-offs and Alternatives

For undo of large state, Memento (Module 48) snapshots state instead of inverting operations. For persistence, commands become events (event sourcing). For simple deferred work, a closure is enough.

Real-World Use

Editor undo/redo, task queues and job schedulers, GUI actions (menu items and toolbar buttons sharing one command), transaction logs, macro recording, robot motion programs, diagnostic command sequences.

Practice

Q1. (Easy) Which participant executes commands without knowing their details?

Answer: The invoker.

Q2. (Medium) Why does SetHeat store previous_ in execute?

Answer: So undo restores the exact prior state, whatever it was.

Q3. (Advanced) How would you log commands to replay them after a crash?

Answer: Serialize each command's type and parameters to an append-only log before (or after) executing it; on restart, replay the log against a fresh receiver. Commands must be deterministic and must not depend on transient references (store IDs instead).

Interview Question

Q1. "Implement undo/redo for a text editor — what pattern and data structures?"

Answer: Command with execute/undo (insert text ↔ delete it, storing position and text), two stacks (done and undone); executing a new command pushes to *done* and clears *undone*; undo pops from done, calls undo, pushes to undone; redo is the mirror. Group keystrokes into one command (coalescing) and use macros for multi-step operations. For huge documents consider Memento with structural sharing.

Quick Quiz

Q1. What does a command encapsulate?

Answer: A request: the action, its receiver and its parameters.

Q2. Which pattern combines with Command for macros?

Answer: Composite.

Q3. What happens to the redo stack when a new command runs?

Answer: It is cleared.

Summary

Command turns requests into objects: queue, log, undo, redo, combine.

Store enough state for exact undo.

Related: Modules 35, 48, 76 (events).