Command Pattern

The Command is a behavioral design pattern that turns a request into a stand-alone object containing all information about the request (receiver, method, arguments). This object can be queued, logged, undone, or executed later, decoupling the invoker (who triggers) from the receiver (who performs).

Key Principles

Structure

Python Example: Text Editor with Undo

from abc import ABC, abstractmethod

# Receiver (performs actual work)
class TextEditor:
    def __init__(self):
        self.text = ""
        self.cursor = 0

    def insert(self, char):
        self.text = self.text[:self.cursor] + char + self.text[self.cursor:]
        self.cursor += 1
        print(f"Inserted '{char}' → {self.text}")

    def delete(self):
        if self.cursor > 0:
            self.text = self.text[:self.cursor-1] + self.text[self.cursor:]
            self.cursor -= 1
            print(f"Deleted → {self.text}")

    def __str__(self):
        return self.text

# Command Interface
class Command(ABC):
    @abstractmethod
    def execute(self):
        pass

    @abstractmethod
    def undo(self):
        pass

# Concrete Commands
class InsertCommand(Command):
    def __init__(self, editor: TextEditor, char: str):
        self.editor = editor
        self.char = char

    def execute(self):
        self.editor.insert(self.char)

    def undo(self):
        self.editor.delete()

class DeleteCommand(Command):
    def __init__(self, editor: TextEditor):
        self.editor = editor

    def execute(self):
        self.editor.delete()

    def undo(self):
        # Undo delete would need saved char – simplified here
        print("Undo delete not implemented in simple example")

# Invoker (executes and manages history)
class EditorInvoker:
    def __init__(self):
        self.history = []

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

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

# Client Code
if __name__ == "__main__":
    editor = TextEditor()
    invoker = EditorInvoker()

    invoker.execute_command(InsertCommand(editor, "H"))
    invoker.execute_command(InsertCommand(editor, "e"))
    invoker.execute_command(InsertCommand(editor, "l"))
    invoker.execute_command(InsertCommand(editor, "l"))
    invoker.execute_command(InsertCommand(editor, "o"))

    print(f"Current text: {editor}")

    invoker.undo()  # Undo last 'o'
    invoker.undo()  # Undo last 'l'

    print(f"After undo: {editor}")

Output:

Inserted 'H' → H
Inserted 'e' → He
Inserted 'l' → Hel
Inserted 'l' → Hell
Inserted 'o' → Hello
Current text: Hello
Deleted → Hell
Deleted → Hel
After undo: Hel

When to Use Command

Summary: Command encapsulates requests as objects, enabling queuing, undo, logging, and decoupling invoker from receiver.