Command: Represent a Request as an Object
A button need not know the details of document editing. Representing a request as an object lets buttons, menus, and histories reuse it. Command separates the requester from the object doing the work.
Remember the previous state
class Document:
def __init__(self, name):
self.name = name
class Rename:
def __init__(self, document, new_name):
self.document = document
self.new_name = new_name
self.previous = None
def execute(self):
self.previous = self.document.name
self.document.name = self.new_name
def undo(self):
if self.previous is None:
raise ValueError("Execute first")
self.document.name = self.previous
self.previous = None
document = Document("old")
command = Rename(document, "new")
command.execute()
print(document.name)
command.undo()
print(document.name)
The outputs are new and old. Rename stores the previous name and restores it in undo. This small example supports one execution followed immediately by one undo. Undo before execution raises an error.
Multiple undo steps require a history of executed commands, commonly a stack used in reverse order. Redo, history size, and concurrent edits require additional design.
Not every action is reversible
A sent email cannot be restored like an in-memory value. External actions need cancellation support or separate compensating operations. Wrapping an action in Command does not automatically provide transactions or exactly-once execution.
Check your understanding
A name changes from A to B, then B to C. In which order should the commands be undone?
Show explanation
Undo B-to-C first to restore B, then A-to-B to restore A. Each command must retain the state immediately before its own execution.