Bird
Raised Fist0
LLDsystem_design~7 mins

Command pattern for undo in LLD - System Design Guide

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Problem Statement
When users perform actions in an application, mistakes happen and they want to revert changes. Without a structured way to track and reverse operations, undoing actions becomes error-prone, inconsistent, or impossible, leading to poor user experience and potential data loss.
Solution
The command pattern encapsulates each user action as an object with methods to execute and undo the action. By storing these command objects in a history stack, the system can call undo on the last executed command to revert its effect, enabling reliable and consistent undo functionality.
Architecture
Client
Command

This diagram shows the client triggering commands that execute actions on receivers. Each command is stored in a history stack to support undo by calling undo() on the last command.

Trade-offs
✓ Pros
Enables clean separation between action execution and undo logic.
Supports complex undo/redo by stacking command objects.
Makes adding new commands easier without changing existing code.
Improves maintainability by encapsulating all action details.
✗ Cons
Increases memory usage due to storing command history.
Adds complexity by requiring command objects for every action.
Undo logic must be carefully implemented for each command to avoid inconsistent states.
Use when your application requires reliable undo/redo functionality with multiple reversible actions and you want to keep code modular and extensible.
Avoid if your system has very simple or stateless actions where undo is trivial or unnecessary, or if memory constraints prohibit storing command history.
Real World Examples
Adobe
Adobe Photoshop uses the command pattern to implement undo/redo for complex image editing operations, allowing users to revert changes step-by-step.
Microsoft
Microsoft Word applies the command pattern to track text editing commands, enabling users to undo and redo typing, formatting, and other document changes.
IntelliJ IDEA
IntelliJ IDEA uses the command pattern to manage undo/redo of code edits and refactorings, ensuring consistent state restoration.
Code Example
The before code shows a simple text editor without undo. The after code applies the command pattern by encapsulating write actions as commands with execute and undo methods. The editor keeps a history stack of commands to support undo by calling undo on the last command.
LLD
### Before: No command pattern, no undo support
class TextEditor:
    def __init__(self):
        self.text = ""

    def write(self, words):
        self.text += words

editor = TextEditor()
editor.write("Hello")
print(editor.text)  # Output: Hello


### After: Command pattern with undo support
class Command:
    def execute(self):
        pass
    def undo(self):
        pass

class WriteCommand(Command):
    def __init__(self, editor, words):
        self.editor = editor
        self.words = words

    def execute(self):
        self.editor.text += self.words

    def undo(self):
        self.editor.text = self.editor.text[:-len(self.words)]

class TextEditor:
    def __init__(self):
        self.text = ""
        self.history = []

    def execute_command(self, command):
        command.execute()
        self.history.append(command)

    def undo(self):
        if self.history:
            command = self.history.pop()
            command.undo()

editor = TextEditor()
cmd1 = WriteCommand(editor, "Hello")
editor.execute_command(cmd1)
print(editor.text)  # Output: Hello
editor.undo()
print(editor.text)  # Output: ""
OutputSuccess
Alternatives
Memento pattern
Stores snapshots of the entire object state rather than encapsulating actions as commands.
Use when: Choose when you need to restore entire object states and actions are hard to reverse individually.
Event sourcing
Stores all changes as a sequence of events that can be replayed to restore state, rather than individual command objects.
Use when: Choose when you want full audit trails and system state reconstruction from event logs.
Summary
The command pattern encapsulates actions as objects with execute and undo methods.
It enables reliable undo functionality by storing executed commands in a history stack.
This pattern improves code modularity and makes adding undo support easier and consistent.

Practice

(1/5)
1. What is the main purpose of the Command pattern in the context of undo functionality?
easy
A. To replace all conditional statements with loops
B. To directly modify the user interface without storing history
C. To store data in a database for permanent record
D. To encapsulate actions as objects with execute and undo methods

Solution

  1. Step 1: Understand the role of Command pattern

    The Command pattern wraps actions as objects, allowing them to be executed and undone independently.
  2. Step 2: Relate to undo functionality

    This wrapping enables storing commands in a history stack, so undo can call the undo method on the last command.
  3. Final Answer:

    To encapsulate actions as objects with execute and undo methods -> Option D
  4. Quick Check:

    Command pattern = encapsulate actions for undo [OK]
Hint: Command pattern wraps actions for undo/redo [OK]
Common Mistakes:
  • Thinking Command pattern modifies UI directly
  • Confusing Command pattern with data storage
  • Assuming it replaces loops or conditionals
2. Which method signature correctly belongs to a Command interface supporting undo?
easy
A. void save(); void load();
B. void execute(); void undo();
C. void start(); void finish();
D. void run(); void stop();

Solution

  1. Step 1: Identify standard Command interface methods

    The Command pattern typically defines an execute() method to perform the action and an undo() method to reverse it.
  2. Step 2: Match method signatures

    Only void execute(); void undo(); has execute() and undo(), matching the Command pattern for undo.
  3. Final Answer:

    void execute(); void undo(); -> Option B
  4. Quick Check:

    Command methods = execute and undo [OK]
Hint: Look for execute() and undo() methods [OK]
Common Mistakes:
  • Choosing unrelated method names like run/stop
  • Confusing start/finish with undo functionality
  • Assuming save/load are Command methods
3. Given the following code snippet, what will be the output after calling undo() on the last command?
class AddCommand:
    def __init__(self, value, receiver):
        self.value = value
        self.receiver = receiver
    def execute(self):
        self.receiver.total += self.value
    def undo(self):
        self.receiver.total -= self.value

class Receiver:
    def __init__(self):
        self.total = 0

receiver = Receiver()
cmd1 = AddCommand(5, receiver)
cmd2 = AddCommand(3, receiver)
cmd1.execute()
cmd2.execute()
cmd2.undo()
print(receiver.total)
medium
A. 5
B. 8
C. 3
D. 0

Solution

  1. Step 1: Trace command executions

    Initially, receiver.total = 0. After cmd1.execute(), total = 0 + 5 = 5. After cmd2.execute(), total = 5 + 3 = 8.
  2. Step 2: Apply undo on cmd2

    cmd2.undo() subtracts 3, so total = 8 - 3 = 5.
  3. Final Answer:

    5 -> Option A
  4. Quick Check:

    Execute adds, undo subtracts = 5 [OK]
Hint: Undo reverses last execute effect on total [OK]
Common Mistakes:
  • Forgetting to subtract on undo
  • Assuming undo resets total to zero
  • Mixing order of execute and undo
4. Identify the bug in this undo implementation of a Command pattern:
class MultiplyCommand:
    def __init__(self, value, receiver):
        self.value = value
        self.receiver = receiver
        self.prev = None
    def execute(self):
        self.prev = self.receiver.total
        self.receiver.total *= self.value
    def undo(self):
        self.receiver.total /= self.value

receiver = type('Receiver', (), {'total': 10})()
cmd = MultiplyCommand(2, receiver)
cmd.execute()
cmd.undo()
print(receiver.total)
medium
A. Execute should add instead of multiply
B. Undo method is missing
C. Undo should restore previous value, not divide
D. Receiver class is not defined

Solution

  1. Step 1: Analyze execute and undo methods

    Execute saves previous total and multiplies current total by value. Undo divides total by value.
  2. Step 2: Identify problem with undo

    Undo divides by value, but if value is zero or changed, this may not restore original total exactly. It should restore saved previous total instead.
  3. Final Answer:

    Undo should restore previous value, not divide -> Option C
  4. Quick Check:

    Undo must restore saved state, not recalculate [OK]
Hint: Undo must restore saved state, not recalculate [OK]
Common Mistakes:
  • Assuming division always reverses multiplication
  • Not saving previous state before execute
  • Ignoring edge cases like zero multiplication
5. You are designing a text editor with undo using the Command pattern. Which approach best supports multiple undo and redo operations efficiently?
hard
A. Use two stacks: one for undo commands, one for redo commands
B. Store all commands in a single list without pointers
C. Only keep the last command for undo, discard others
D. Save full document snapshots after each command

Solution

  1. Step 1: Understand undo/redo requirements

    Undo reverses last command, redo reapplies commands undone. Efficient support requires tracking both undo and redo history.
  2. Step 2: Evaluate data structures

    Two stacks allow pushing commands on execute, popping for undo, and pushing undone commands to redo stack. This supports multiple undo/redo efficiently.
  3. Final Answer:

    Use two stacks: one for undo commands, one for redo commands -> Option A
  4. Quick Check:

    Two stacks = efficient undo/redo [OK]
Hint: Two stacks handle undo and redo efficiently [OK]
Common Mistakes:
  • Using single list without tracking position
  • Keeping only last command loses history
  • Saving full snapshots wastes memory