Mr. Latte


Lesson 7 of 13

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.

Looking for a product partner? Founders, teams, businesses: from problem framing to launch.