Command Pattern: Turn Every Action into an Order Slip
Learn the Command pattern through a restaurant order slip story. Simple TypeScript and C# code with undo and redo, diagrams, tables, and practice tasks.
🍛 One plate of paneer butter masala, please
Meet Aarav. It is a busy Sunday evening, and Aarav has brought his family to Hotel Annapurna, a famous little restaurant in Mumbai. They squeeze into table 4. The waiter, Ravi, comes over with a smile and a small notepad. Aarav orders: "One paneer butter masala, two butter naans, and one sweet lassi."
Does Ravi run to the stove and start cooking? No! He writes the order on a slip. He tears off the slip and clips it onto a steel rail at the kitchen window. Inside, Chef Lakshmi picks up slips one by one and cooks whatever each slip says. She has never seen Aarav. Aarav has never seen her. The slip connects them.
Stop and look carefully at that little paper slip. It is doing something quietly magical:
- Aarav (the customer) never talks to the chef. He does not know who is cooking tonight.
- Ravi carries the slip but cannot cook. He does not even need to understand recipes.
- The slip holds everything needed: which dish, how many, which table. It can wait in a queue on the rail. If a plate falls, Chef Lakshmi can cook it again from the same slip. At closing time, the owner, Mr. D'Souza, counts all the slips to know the day's business.
- Chef Lakshmi reads the slip and does the real work.
And one more thing — halfway through the evening, Aarav's little sister changes her mind: "Bhaiya, cancel the lassi!" Ravi simply pulls that slip off the rail. The order is undone before it ever reaches the stove. Later she changes her mind again, and Ravi clips the same slip back. The order is redone. No drama, no rewriting — the slip was already there.
That order slip is the Command pattern. A request, written down as an object, that can travel, wait, repeat, and even be cancelled. Hold on to this restaurant story — every section below follows Aarav's table 4.
Here is the evening at a glance:
And here is the same evening told as a journey:
🎯 What is the Command pattern?
Command is a behavioral design pattern from the Gang of Four (GoF) book (1994), where it is also called Action or Transaction.
Here is a definition you can keep for revision:
The Command pattern turns a request into a standalone object. The object stores everything about the request — the receiver, the operation, and its parameters — behind one tiny interface, usually a single
execute()method (and often anundo()method too).
Once a request becomes an object, you can do things that are impossible with a plain method call:
- Store it in a variable or a list, like slips on the rail.
- Queue it to run later, or on another thread, like slips waiting their turn.
- Log it to disk, so a crashed program can replay its history, like Mr. D'Souza counting slips at night.
- Undo it, because the object can remember how to reverse itself, like pulling a slip off the rail.
The pattern has five roles. Match them with the restaurant:
| Role | Job | Restaurant version |
|---|---|---|
| Command | Interface with execute() (and undo()) | The idea of "an order slip" |
| ConcreteCommand | One real request; stores receiver + details | A filled slip: "1 paneer, table 4" |
| Invoker | Triggers commands; never cooks | Ravi and the kitchen rail |
| Receiver | Does the actual work | Chef Lakshmi |
| Client | Creates commands and wires everything | Aarav, the customer ordering |
Memory trick: "Write it, don't shout it." Instead of shouting your order at the chef (a direct method call), you write a slip (a command object). Anything written down can wait, be repeated, be counted, and be torn up. That is the whole power of this pattern in one line.
The whole pattern fits on one mind map. Redraw it from memory before your exam:
😖 The problem it solves
Let us see the pain without the pattern. Suppose Mr. D'Souza hires you to build the restaurant's billing screen. It has buttons: Add Item, Remove Item, Apply Discount. The lazy way is to put the work directly inside each button:
// ❌ Each button directly does the work — tight coupling everywhere
addItemButton.onClick = () => {
bill.items.push({ name: "Paneer Butter Masala", price: 320 });
screen.refresh();
};
removeItemButton.onClick = () => {
bill.items.pop();
screen.refresh();
};Looks harmless. Now the problems arrive one by one, just like Sunday customers:
- The keyboard shortcut problem. Mr. D'Souza wants
Ctrl+Zto undo the last action. But the actions were never recorded anywhere! The work happened and vanished — like a shouted order that nobody wrote down. There is nothing to undo. - The duplicate logic problem. The same "add item" must now also work from a menu entry and from a touchscreen. You copy-paste the logic into three places. One day you fix a bug in one place and forget the other two.
- The queue problem. During rush hour, orders should go into a queue and run one by one. But a button click is not a thing you can put into a queue. It just happens instantly.
- The history problem. Mr. D'Souza asks, "Show me every action taken today." You have no record, because actions were never objects — they were just method calls that disappeared.
The deep issue is coupling: the thing that triggers work (button) is glued to the thing that does work (bill). There is no layer in between where a request can be held, stored, or reversed. The restaurant without slips is a restaurant where customers shout at the chef — chaos at 8 pm, and no record at 11 pm.
The Command pattern inserts the missing layer: every trigger creates or holds a command object, and only the command talks to the receiver. The invoker also pushes each executed command onto a history stack — and suddenly undo, logging, and queues become easy.
⚙️ How it works, step by step
Let us assemble the pattern piece by piece, using the restaurant.
Step 1: Declare the Command interface. Two methods: execute() and undo().
Step 2: Identify the receiver. The class that already knows the real work — our Kitchen (or in an editor, the Document). Do not move business logic into commands. Chef Lakshmi cooks; the slip never cooks. Commands only delegate.
Step 3: Write one concrete command per request type. PrepareDishCommand, CancelDishCommand. Each stores its receiver and its parameters in the constructor, so it is fully ready to run — a filled slip, not a blank one.
Step 4: Build the invoker. Our Waiter holds commands, runs them, and keeps a history stack. After execute(), push the command onto the stack. For undo, pop and call undo().
Step 5: Wire it in the client. Create the kitchen, create commands around it, hand them to the waiter.
Watch one order travel through the system, message by message:
Ravi's code is beautifully ignorant. He runs execute() on whatever slip he holds — paneer, biryani, or dessert — all look identical to him. That uniformity is exactly why the same invoker can also power queues and undo stacks.
The undo machinery follows four small rules. Learn them — interviewers love them:
execute a command -> push it onto the UNDO stack
undo -> pop UNDO, call undo(), push onto REDO stack
redo -> pop REDO, call execute(), push onto UNDO stack
new command after undo -> CLEAR the redo stackWhy clear the redo stack? Imagine Aarav undoes "add lassi", then orders "add gulab jamun". Redoing "add lassi" now would replay a future that no longer makes sense — the evening has moved on. Clearing keeps history honest.
A single command also lives a small life of its own. Here it is as states:
College corner: the undo stack you are about to build is the exact machinery inside real editors, but production systems add limits and tricks. Editors cap the stack depth (say, the last 200 commands) so memory stays bounded. They coalesce tiny commands — typing "hello" becomes one command, not five, so one Ctrl+Z removes the word, not one letter. And they distinguish two undo styles: inverse-operation undo (the command knows its reverse, cheap on memory) versus snapshot undo (store the old state before executing — the Memento pattern — heavier but works when an action has no clean mathematical reverse, like "apply blur filter"). Most large apps mix both.
💻 Real-life code example
Here is the full restaurant in TypeScript — with working undo and redo. Read the comments; they carry the story.
// ----- The RECEIVER: the kitchen does the real work -----
class Kitchen {
private board: string[] = []; // dishes currently being prepared
prepare(dish: string): void {
this.board.push(dish);
console.log(`Kitchen: started preparing ${dish}.`);
}
stopPreparing(dish: string): void {
const index = this.board.lastIndexOf(dish);
if (index !== -1) {
this.board.splice(index, 1);
console.log(`Kitchen: cancelled ${dish}.`);
}
}
showBoard(): void {
console.log(`Kitchen board: [${this.board.join(", ")}]`);
}
}
// ----- The COMMAND interface: every order slip looks the same -----
interface Command {
execute(): void;
undo(): void;
}
// ----- A CONCRETE COMMAND: one filled order slip -----
class PrepareDishCommand implements Command {
// The slip stores the receiver AND the parameters.
constructor(
private kitchen: Kitchen,
private dish: string,
) {}
execute(): void {
this.kitchen.prepare(this.dish); // delegate to the receiver
}
undo(): void {
this.kitchen.stopPreparing(this.dish); // the reverse action
}
}
// ----- The INVOKER: the waiter runs slips and keeps history -----
class Waiter {
private undoStack: Command[] = [];
private redoStack: Command[] = [];
place(command: Command): void {
command.execute();
this.undoStack.push(command);
this.redoStack = []; // a fresh action clears the redo future
}
undo(): void {
const command = this.undoStack.pop();
if (!command) {
console.log("Waiter: nothing to undo.");
return;
}
command.undo();
this.redoStack.push(command);
}
redo(): void {
const command = this.redoStack.pop();
if (!command) {
console.log("Waiter: nothing to redo.");
return;
}
command.execute();
this.undoStack.push(command);
}
}
// ----- The CLIENT: Aarav orders -----
const kitchen = new Kitchen();
const waiter = new Waiter();
waiter.place(new PrepareDishCommand(kitchen, "Paneer Butter Masala"));
waiter.place(new PrepareDishCommand(kitchen, "Butter Naan"));
waiter.place(new PrepareDishCommand(kitchen, "Sweet Lassi"));
kitchen.showBoard();
console.log("\nAarav's sister: cancel the lassi please!");
waiter.undo(); // undo last order
kitchen.showBoard();
console.log("\nAarav's sister: actually, bring the lassi back!");
waiter.redo(); // redo it
kitchen.showBoard();Output:
Kitchen: started preparing Paneer Butter Masala.
Kitchen: started preparing Butter Naan.
Kitchen: started preparing Sweet Lassi.
Kitchen board: [Paneer Butter Masala, Butter Naan, Sweet Lassi]
Aarav's sister: cancel the lassi please!
Kitchen: cancelled Sweet Lassi.
Kitchen board: [Paneer Butter Masala, Butter Naan]
Aarav's sister: actually, bring the lassi back!
Kitchen: started preparing Sweet Lassi.
Kitchen board: [Paneer Butter Masala, Butter Naan, Sweet Lassi]Notice three lovely things:
- The waiter class never mentions food. It would work unchanged for a drawing app (
DrawCircleCommand) or a smart home (LightsOnCommand). Invokers are fully reusable. - Undo cost us almost nothing. Each command knows its own reverse, and the waiter keeps two stacks. That is the entire machinery behind
Ctrl+Zin real editors. - A macro is trivial. A "Family Combo" command can hold a list of commands and run them all — a command made of commands (Command + Composite):
class ComboCommand implements Command {
constructor(private commands: Command[]) {}
execute(): void {
this.commands.forEach((c) => c.execute());
}
undo(): void {
// undo in REVERSE order — last action reversed first
[...this.commands].reverse().forEach((c) => c.undo());
}
}
waiter.place(
new ComboCommand([
new PrepareDishCommand(kitchen, "Veg Biryani"),
new PrepareDishCommand(kitchen, "Raita"),
]),
);
waiter.undo(); // cancels Raita first, then Biryani — one clean undoWatch how the history stacks breathe as you work. Every edit grows the undo stack; every undo shrinks it (and grows redo):
Look at the last bar carefully: after the new edit, the count is 9, not 10. The two commands still sitting on the redo stack were cleared by the new action — rule four in our text box, working exactly as promised.
🟦 The same idea in C#
C# makes the pattern feel right at home — WPF and other UI frameworks even ship a built-in ICommand interface. Here is our restaurant, shortened:
public interface ICommandSlip
{
void Execute();
void Undo();
}
// Receiver
public class Kitchen
{
private readonly List<string> _board = new();
public void Prepare(string dish)
{
_board.Add(dish);
Console.WriteLine($"Kitchen: preparing {dish}.");
}
public void StopPreparing(string dish)
{
_board.Remove(dish);
Console.WriteLine($"Kitchen: cancelled {dish}.");
}
}
// Concrete command
public class PrepareDishCommand : ICommandSlip
{
private readonly Kitchen _kitchen;
private readonly string _dish;
public PrepareDishCommand(Kitchen kitchen, string dish)
=> (_kitchen, _dish) = (kitchen, dish);
public void Execute() => _kitchen.Prepare(_dish);
public void Undo() => _kitchen.StopPreparing(_dish);
}
// Invoker with history
public class Waiter
{
private readonly Stack<ICommandSlip> _history = new();
public void Place(ICommandSlip slip)
{
slip.Execute();
_history.Push(slip);
}
public void UndoLast()
{
if (_history.TryPop(out var slip)) slip.Undo();
}
}
// Client
var kitchen = new Kitchen();
var waiter = new Waiter();
waiter.Place(new PrepareDishCommand(kitchen, "Masala Dosa"));
waiter.Place(new PrepareDishCommand(kitchen, "Filter Coffee"));
waiter.UndoLast(); // cancels Filter CoffeeAnd the same skeleton in Python, for completeness — commands are just objects with two methods:
class PrepareDishCommand:
def __init__(self, kitchen, dish):
self.kitchen = kitchen
self.dish = dish
def execute(self):
self.kitchen.prepare(self.dish)
def undo(self):
self.kitchen.stop_preparing(self.dish)
class Waiter:
def __init__(self):
self.history = []
def place(self, command):
command.execute()
self.history.append(command)
def undo_last(self):
if self.history:
self.history.pop().undo()In modern C#, TypeScript, and Python, very simple commands can even be plain lambdas (() => kitchen.Prepare("Dosa")). That removes class ceremony — but the moment you need undo() or serialization, a proper command class earns its keep.
Here is the full class structure of what we built:
🌍 Where you see it in real software
Once you know this pattern, you will spot order slips everywhere.
1. Undo/redo in editors. Microsoft Word, Google Docs, Photoshop, and VS Code all track your edits as command-like objects on history stacks. Press Ctrl+Z and the editor pops the last command and reverses it. Behind every undo arrow is a rail of slips.
2. Job and task queues. Background job systems (think email senders, report generators, message queues) serialize "what to do" into a stored object, push it onto a queue, and a worker executes it later — maybe on a different machine. A job is a command that learned to travel.
3. Redux actions. In Redux, every change to the app's state is a plain action object like a typed note saying "ADD_ITEM with this payload" — a request turned into data. Because actions are objects, tools like redux-undo can give whole apps undo/redo almost for free, and the Redux DevTools can log and replay every action (that replay is "time-travel debugging").
4. GUI buttons and menus. Classic desktop frameworks (Java Swing's Action, WPF's ICommand) let one command power a toolbar button, a menu item, and a keyboard shortcut at once — fixing the duplicate-logic problem we met earlier.
5. Transactions and recovery. Databases and some servers write each operation to a log before applying it. After a crash, they replay the logged commands to rebuild the latest consistent state. "Transaction" is literally one of this pattern's official alternate names.
6. Smart home routines. "Good night" on a voice assistant runs a macro command: lights off, doors locked, AC to 24 degrees. One trigger, many slips.
| Real software | The command object | What the pattern enables |
|---|---|---|
| Word / Docs / VS Code | An edit operation | Undo/redo stacks |
| Background job queues | A serialized job | Run later, retry, run elsewhere |
| Redux | An action object | Logging, time travel, redux-undo |
| WPF / Swing UIs | ICommand / Action | One action, many triggers |
| Databases | A logged transaction | Crash recovery by replay |
| Smart home scenes | A routine of device commands | Macros from one trigger |
College corner: push the "log every command" idea to its limit and you get event sourcing. In an event-sourced system, the log of commands and the events they produce is the database. Your bank account is not stored as one number — it is the replay of every deposit and withdrawal since the account opened. Need the balance? Replay the slips. Need the balance as it was last March? Replay the slips up to last March. Mr. D'Souza already does this: his pile of order slips, replayed at midnight, is his accounts book. Frameworks like EventStoreDB and patterns like CQRS build entire architectures on this one insight: if every change is an object, history becomes data you can query, audit, and time-travel through.
At closing time, Mr. D'Souza sorts the day's slips. Most got cooked; a few were cancelled; a couple were cancelled and then brought back, like the famous lassi:
🤔 When to use it and when not to
| Situation | Use it? | Why |
|---|---|---|
| You need undo/redo | ✅ | The classic, number-one reason |
| Operations must be queued, scheduled, or run on another thread | ✅ | Commands are objects — objects can wait |
| You want a log/history of everything that happened | ✅ | Push every executed command to a list |
| One action must fire from button, menu, and shortcut | ✅ | One command, many invokers |
| You want macro operations (one click = many steps) | ✅ | A composite command holds child commands |
| A button does one fixed thing, forever, with no history needed | ❌ | A direct call or simple callback is enough |
| You are wrapping every tiny getter in a command "for purity" | ❌ | Pure ceremony; patterns must solve real pain |
| The operation cannot be reversed and you promise undo anyway | ⚠️ | Use snapshots (Memento) or do not promise undo |
Place your own feature on this map. The more your actions need a "later" (undo later, run later, audit later), the stronger the case for slips:
⚠️ Common mistakes students make
Mistake 1: Cooking inside the slip. Students often move real business logic into the command class. Wrong! Chef Lakshmi cooks; the slip only says what to cook. Commands should delegate to the receiver. If your command is 200 lines long, the chef's work has leaked into the paper.
Mistake 2: Forgetting to clear the redo stack. After an undo, if the user performs a brand-new action, you must clear the redo stack. If you forget, the user can "redo" into a timeline that no longer exists, and your app's state becomes nonsense. This is the single most common undo/redo bug.
More traps to avoid:
- Undoing a macro in forward order. A combo of [add biryani, add raita] must undo as [remove raita, remove biryani] — always reverse the order. Think of stacking plates: the last plate placed comes off first.
- Commands sharing mutable state. If two commands both hold references to the same changing data, undoing one can corrupt the other's idea of "before". Keep each command's undo data inside itself, like each slip carrying its own table number.
- An invoker that builds commands. Ravi should never decide the menu. Invokers receive commands from the client; they do not construct them.
- Unbounded history. Pushing every command forever eats memory. Real editors cap history depth (for example, keep the last 100 commands). The rail has finite space; old slips go into the box.
- Half-done executes. If
execute()fails halfway, pushing it onto the undo stack anyway meansundo()will reverse work that never fully happened. Push only after a successful execute.
👪 Compare with cousins
Command sits in a family of "wrap behavior in an object" patterns. Here is how to tell them apart:
| Pattern | What is wrapped? | Main question it answers | Special power |
|---|---|---|---|
| Command | A request: what to do + receiver + params | "Can I store/queue/undo this action?" | History, queues, undo |
| Strategy | An algorithm: how to do one job | "Which way should this task be done?" | Swappable algorithms |
| Chain of Responsibility | Nothing — it links handlers | "Who will handle this request?" | Finds a handler at runtime |
| Memento | A snapshot of state | "What did things look like before?" | State restore (pairs with Command for undo) |
| Observer | A subscription list | "Who wants to know this happened?" | Broadcast to many listeners |
The Strategy confusion is the most common. Both look like "an object with one method". The test: Strategy answers how ("sort with quicksort or mergesort?"), Command answers what ("insert text", "delete row") and adds storage, queuing, and undo. In restaurant terms: a Strategy is Chef Lakshmi choosing between two recipes for the same dosa; a Command is the slip saying a dosa is wanted at table 4.
Also remember the friendly pairings: Command + Composite = macro commands (the Family Combo); Command + Memento = snapshot undo (photograph the kitchen board before cooking, restore the photo to undo); and a queue of commands is usually walked by an Iterator — the next lesson in this series.
📦 Quick revision box
+=====================================================================+
| COMMAND PATTERN — QUICK REVISION |
+=====================================================================+
| WHAT : Turn a request into an object with execute() + undo(). |
| STORY : Restaurant order slip. Aarav=Client, Ravi=Invoker, |
| Slip=Command, Chef Lakshmi/Kitchen=Receiver. |
| ROLES : Command | ConcreteCommand | Invoker | Receiver | Client |
| RULE : Commands DELEGATE to receivers; they do not do the work. |
| UNDO : execute -> push UNDO | undo -> pop UNDO, push REDO |
| redo -> pop REDO, push UNDO | new action -> CLEAR REDO |
| WINS : undo/redo, queues, logging/replay, macros, one action |
| from many triggers |
| RISKS : class explosion, indirection, tricky undo state |
| SEEN IN : editor Ctrl+Z, job queues, Redux actions, WPF ICommand, |
| database transaction logs, event sourcing |
| COUSIN : Strategy = HOW to do a job; Command = WHAT to do, plus |
| store / queue / undo. |
+=====================================================================+🏋️ Practice exercise
Type these out yourself — typing builds memory that reading never will.
Task 1: TV remote. Build a Television receiver with on(), off(), setChannel(n). Create PowerOnCommand, PowerOffCommand, and SetChannelCommand (it must remember the previous channel so undo() can go back). Build a RemoteControl invoker with a history stack and an undoLast() button. Test: power on → channel 5 → channel 9 → undo (back to 5) → undo (does the TV remember what to reverse?).
Task 2: Text editor with redo. Build a Document receiver with insert(text) and deleteLast(count). Create a TypeCommand and give your invoker full undo and redo stacks, including the "clear redo on new action" rule. Test: type "Namaste", type " India", undo, undo, redo — the document should read "Namaste".
Task 3 (challenge): Order queue with replay. Make your restaurant waiter store every placed command in a log: Command[]. Add a replayDay(kitchen) function that builds a fresh kitchen and re-executes every logged command in order, rebuilding the kitchen board exactly. You have just implemented the core idea behind database crash recovery, Redux time-travel debugging, and event sourcing — Mr. D'Souza's midnight slip-counting, written in code.
Finish all three and you will never look at Ctrl+Z the same way again — behind that little shortcut is a rail of order slips, quietly waiting to be torn up. 🧾
Frequently asked questions
- What is the Command pattern in simple words?
- It wraps a request — what to do, on which object, with which details — into a standalone object with an execute() method. That object can be stored, queued, logged, passed around, or undone later.
- Why is the Command pattern so good for undo and redo?
- Because every action becomes an object that can also remember how to reverse itself. The app keeps executed commands on a history stack, so undo just pops the last command and calls its undo() method.
- What are the invoker and the receiver in the Command pattern?
- The invoker is the thing that triggers the command, like a button or a waiter — it only calls execute(). The receiver is the object that does the real work, like the document or the kitchen.
- Where is the Command pattern used in real software?
- Undo/redo in editors like Word, Google Docs, and VS Code; job queues that run tasks later; Redux actions in front-end apps; and GUI buttons and menu items in many frameworks.
- How is Command different from Strategy?
- Both wrap behavior in an object. Strategy swaps how one job is done (different algorithms for the same task). Command wraps what to do, so the request itself can be queued, logged, or undone.
Further reading
Related Lessons
Chain of Responsibility Pattern: Pass the Request Until Someone Handles It
Learn the Chain of Responsibility pattern with a simple school leave application story, easy TypeScript and C# code, diagrams, tables, and practice tasks.
Iterator Pattern: Visit Every Element, One Seat at a Time
Learn the Iterator pattern with a railway ticket checker story. Build a custom iterator in TypeScript, use for...of and generators, plus C# IEnumerable.
Memento Pattern: Save Your Game Before the Boss Fight
Understand the Memento pattern through a video game save point story, with TypeScript and Python undo examples, diagrams, tables and easy practice tasks.
Strategy Pattern: Cycle, Bus, or Auto — You Choose
Learn the Strategy design pattern with a simple school travel story, easy TypeScript and C# code, runtime swapping, real examples, and practice tasks.