Suggest an editImprove this articleRefine the answer for “What is Command design pattern?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Command design pattern** turns an action into an object with `execute()` and `undo()` methods. ```javascript const cmd = new LightOnCommand(light); cmd.execute(); // Light on ``` The sender calls `execute()` without knowing who the receiver is. **Key:** wraps a request so it can be queued, deferred, or reversed.Shown above the full answer for quick recall.Answer (EN)Image**Command design pattern** turns an action into an object you can store, pass around, queue, or reverse. ## Theory ### TL;DR - Restaurant analogy: the waiter hands an order slip (command object) to the kitchen (receiver). The kitchen executes without knowing the customer. - Core idea: wrap a request in an object so the caller and the executor know nothing about each other. - Both `execute()` and `undo()` live on the command object, making any action reversible. - Use it when you need undo/redo, task queues, or macro commands. - For simple one-off calls with no queue or undo, skip it and call the method directly. ### Quick example ```javascript class Light { on() { console.log('Light on'); } off() { console.log('Light off'); } } class LightOnCommand { constructor(light) { this.light = light; } execute() { this.light.on(); } // Light on } class RemoteControl { setCommand(cmd) { this.slot = cmd; } pressButton() { this.slot.execute(); } } const remote = new RemoteControl(); remote.setCommand(new LightOnCommand(new Light())); remote.pressButton(); // Light on ``` `RemoteControl` knows nothing about `Light`. It only calls `execute()`. Replace `LightOnCommand` with a `DimmerOnCommand` later and the remote stays unchanged. ### Key difference from direct method calls When you call `light.on()` directly from the remote, the remote and the light are coupled. Add a dimmer and you rewrite the remote. With Command, the remote holds a command object and calls `execute()`. The sender never changes when the receiver does. ### When to use - Undo/redo needed: Command stores enough state in the constructor to reverse itself. - Task queues: actions become plain objects you can serialize and defer. - Macro commands: chain multiple commands into one composite action. - Logging and transactions: wrap execute with pre/post hooks. - Simple one-off call with no queue or undo: skip Command. The object allocation adds nothing here. ### How it works in JavaScript Commands are plain objects in V8. The `execute()` call dispatches via prototypal inheritance, no special runtime magic needed. State capture happens through closures and object references stored in the constructor. Node.js event loop queues commands naturally with `setImmediate`; browser UI commands often use `requestAnimationFrame`. ### Common mistakes **1. Forgetting to store state in the constructor** ```javascript // Wrong: this.text is undefined in undo class AddTextCommand { execute() { editor.add('hello'); } undo() { editor.remove(this.text); } // TypeError: undefined } // Right class AddTextCommand { constructor(editor, text) { this.editor = editor; this.text = text; // stored for undo } execute() { this.editor.add(this.text); } undo() { this.editor.remove(this.text); } } ``` **2. Mutating shared state without a snapshot** ```javascript // Wrong: undo pops but re-execute grows the array again constructor(editor) { this.editor = editor; } // no backup // Right: snapshot on construction constructor(editor, text) { this.backup = [...editor.text]; // saved before execute } undo() { this.editor.text = this.backup; } // full restore ``` This is the most common undo-queue bug you will see in production. The array mutates across re-executes and the undo logic silently drifts. **3. Missing null-check on history.pop()** ```javascript // Crashes on empty history undo() { const cmd = history.pop(); cmd.undo(); } // Safe undo() { const cmd = history.pop(); cmd?.undo(); } ``` **4. Overusing Command for simple actions** One function, no queue, no undo? Call it directly. Each command object costs an allocation and a GC cycle. In tight loops that adds up. ### Real-world usage - Redux: every dispatched action is a command object; reducers are the receivers. - BullMQ (Node.js): queued jobs are command objects with built-in retry logic. - Figma and Photoshop: the entire undo history is a command stack. - React use-undo library: wraps state changes as reversible commands. - jQuery UI: uses commands internally for sortable drag-and-drop undo. ### Follow-up questions **Q:** How would you add undo to the canvas example? **A:** Store a snapshot of state in the command constructor. In `undo()`, restore from that snapshot instead of trying to guess the reverse operation. **Q:** How does Command enable macro commands? **A:** A `MacroCommand` holds an array of commands. `execute()` loops forward; `undo()` loops in reverse. If one command throws, roll back all already-executed ones before rethrowing. **Q:** What is the difference between Command and Strategy? **A:** Strategy swaps an algorithm at runtime but executes it immediately. Command wraps a request so it can be deferred, queued, or undone. Strategy is about *how*; Command is about *when* and *whether*. **Q:** How do you handle async commands in Node.js? **A:** Return a Promise from `execute()` and await it in the invoker. The pattern stays the same; you just need to handle partial rollback if a command in a macro chain rejects. **Q:** Design an undoable shopping cart with discounts applied. **A:** Each action (add item, apply discount) becomes a command. Snapshot totals before each execute. Rollback restores the previous snapshot. For concurrent updates, add a version counter to each command and reject stale ones on conflict. ## Examples ### Basic: remote control ```javascript class Light { on() { console.log('Light on'); } off() { console.log('Light off'); } } class LightOnCommand { constructor(light) { this.light = light; } execute() { this.light.on(); } undo() { this.light.off(); } } class LightOffCommand { constructor(light) { this.light = light; } execute() { this.light.off(); } undo() { this.light.on(); } } class RemoteControl { constructor() { this.history = []; } press(cmd) { cmd.execute(); this.history.push(cmd); } undo() { this.history.pop()?.undo(); } } const remote = new RemoteControl(); const light = new Light(); remote.press(new LightOnCommand(light)); // Light on remote.press(new LightOffCommand(light)); // Light off remote.undo(); // Light on (reversed) ``` The remote holds command objects, not a direct reference to the light. Add any new command and the remote code stays the same. ### Intermediate: canvas with undo history ```javascript class Canvas { constructor() { this.elements = []; } add(shape) { this.elements.push(shape); console.log(`Added ${shape}`); } remove(shape) { this.elements = this.elements.filter(s => s !== shape); console.log(`Removed ${shape}`); } } class AddShapeCommand { constructor(canvas, shape) { this.canvas = canvas; this.shape = shape; } execute() { this.canvas.add(this.shape); } undo() { this.canvas.remove(this.shape); } } class History { constructor() { this.stack = []; } execute(cmd) { cmd.execute(); this.stack.push(cmd); } undo() { this.stack.pop()?.undo(); } } const canvas = new Canvas(); const history = new History(); history.execute(new AddShapeCommand(canvas, 'circle')); // Added circle history.execute(new AddShapeCommand(canvas, 'square')); // Added square history.undo(); // Removed square console.log(canvas.elements); // ['circle'] ``` This is roughly how Figma tracks drawing history. Each user action becomes one command on the stack. ### Advanced: transactional macro command ```javascript class MacroCommand { constructor(commands) { this.commands = commands; } execute() { const done = []; for (const cmd of this.commands) { try { cmd.execute(); done.push(cmd); } catch (e) { done.slice().reverse().forEach(c => c.undo()); // rollback throw e; } } } undo() { this.commands.slice().reverse().forEach(cmd => cmd.undo()); } } class ApiCallCommand { constructor(id) { this.id = id; } execute() { if (Math.random() < 0.3) throw new Error('Network timeout'); console.log(`Processed ${this.id}`); } undo() { console.log(`Rolled back ${this.id}`); } } const macro = new MacroCommand([ new ApiCallCommand(1), new ApiCallCommand(2), new ApiCallCommand(3), ]); try { macro.execute(); } catch (e) { console.log('Macro failed, all changes rolled back'); } ``` Reverse-order undo matters. If step 3 fails after steps 1 and 2 succeeded, you undo 2 first, then 1. That is exactly how database transactions handle partial failure too.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.