What if every action you take could be reversed perfectly with just one click?
Why Command pattern for undo in LLD? - Purpose & Use Cases
Start learning this pattern below
Jump into concepts and practice - no test required
Imagine you are editing a document by hand, making changes with pen and paper. If you make a mistake, you have to erase everything manually or start over. There is no easy way to go back step-by-step.
Manually tracking every change is slow and confusing. You might forget what you changed or how to reverse it. This leads to errors and frustration, especially when changes are complex or frequent.
The Command pattern wraps each action as an object with a way to undo it. This lets the system remember and reverse actions easily, like having a magic undo button that works reliably every time.
if (lastChange == 'addText') { removeText(); } else if (lastChange == 'deleteText') { addText(); }
command.execute(); undoStack.push(command); ... lastCommand = undoStack.pop(); lastCommand.undo();
This pattern enables smooth, reliable undo and redo features that improve user experience and reduce errors.
Text editors like Microsoft Word or Google Docs use this pattern to let users undo typing, formatting, or deleting with a simple shortcut.
Manual undo is error-prone and hard to manage.
Command pattern encapsulates actions and their undo logic.
It makes undo/redo features easy, reliable, and scalable.
Practice
Command pattern in the context of undo functionality?Solution
Step 1: Understand the role of Command pattern
The Command pattern wraps actions as objects, allowing them to be executed and undone independently.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.Final Answer:
To encapsulate actions as objects with execute and undo methods -> Option DQuick Check:
Command pattern = encapsulate actions for undo [OK]
- Thinking Command pattern modifies UI directly
- Confusing Command pattern with data storage
- Assuming it replaces loops or conditionals
Solution
Step 1: Identify standard Command interface methods
The Command pattern typically defines anexecute()method to perform the action and anundo()method to reverse it.Step 2: Match method signatures
Only void execute(); void undo(); hasexecute()andundo(), matching the Command pattern for undo.Final Answer:
void execute(); void undo(); -> Option BQuick Check:
Command methods = execute and undo [OK]
- Choosing unrelated method names like run/stop
- Confusing start/finish with undo functionality
- Assuming save/load are Command methods
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)Solution
Step 1: Trace command executions
Initially, receiver.total = 0. After cmd1.execute(), total = 0 + 5 = 5. After cmd2.execute(), total = 5 + 3 = 8.Step 2: Apply undo on cmd2
cmd2.undo() subtracts 3, so total = 8 - 3 = 5.Final Answer:
5 -> Option AQuick Check:
Execute adds, undo subtracts = 5 [OK]
- Forgetting to subtract on undo
- Assuming undo resets total to zero
- Mixing order of execute and undo
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)Solution
Step 1: Analyze execute and undo methods
Execute saves previous total and multiplies current total by value. Undo divides total by value.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.Final Answer:
Undo should restore previous value, not divide -> Option CQuick Check:
Undo must restore saved state, not recalculate [OK]
- Assuming division always reverses multiplication
- Not saving previous state before execute
- Ignoring edge cases like zero multiplication
Solution
Step 1: Understand undo/redo requirements
Undo reverses last command, redo reapplies commands undone. Efficient support requires tracking both undo and redo history.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.Final Answer:
Use two stacks: one for undo commands, one for redo commands -> Option AQuick Check:
Two stacks = efficient undo/redo [OK]
- Using single list without tracking position
- Keeping only last command loses history
- Saving full snapshots wastes memory
