Command Pattern

Updated Nov 25, 2025

Command Pattern

Intent:

The Command Pattern turns each action (like "turn on TV" or "increase volume") into an object. This lets you save actions, undo them, queue them up, or even log them. Instead of directly calling a method, you create a command object that does the work.

When to Use:

Use the Command Pattern when:

  • You want to be able to undo actions (like undo in a text editor)
  • You want to save actions and do them later (like a queue)
  • You want to log what actions were done
  • You want to separate the button (that triggers action) from the thing that does the action

Key Characteristics:

  • Each action becomes an object (command)
  • Commands can be saved and done later
  • Commands can be undone
  • The button (invoker) doesn't need to know how the action works

Think of it like a remote control - you press a button (command), and it performs an action. You can also undo the last action or save multiple commands to do later. Each button press creates a command object that knows how to do the action and how to undo it.


🎮 Example: Simple Remote Control

Let's say we're building a simple remote control for a TV. The remote has buttons for turning the TV on/off and changing volume. We want to be able to undo the last action.

Without Command Pattern: The remote would directly call TV methods. If you want to undo, you'd need to remember what the previous state was, which gets messy.

With Command Pattern: Each button press creates a command object. The command knows how to do the action AND how to undo it. The remote just stores the last command and can undo it easily. This makes adding undo functionality simple and clean.

Step 1: Define Command Interface

# command_interface.py from abc import ABC, abstractmethod class Command(ABC): @abstractmethod def execute(self): pass # Execute the command @abstractmethod def undo(self): pass # Undo the command

Step 2: Create Receiver (TV)

# tv.py class TV: def __init__(self): self.is_on = False self.volume = 10 def turn_on(self): if not self.is_on: self.is_on = True print("[TV] TV turned ON") else: print("[TV] TV is already ON") def turn_off(self): if self.is_on: self.is_on = False print("[TV] TV turned OFF") else: print("[TV] TV is already OFF") def increase_volume(self): if self.is_on: self.volume += 1 print(f"[TV] Volume increased to {self.volume}") else: print("[TV] Cannot change volume - TV is OFF") def decrease_volume(self): if self.is_on: self.volume = max(0, self.volume - 1) print(f"[TV] Volume decreased to {self.volume}") else: print("[TV] Cannot change volume - TV is OFF") def get_status(self): status = "ON" if self.is_on else "OFF" return f"TV is {status}, Volume: {self.volume}"

Step 3: Create Turn On Command

# turn_on_command.py from command_interface import Command from tv import TV class TurnOnCommand(Command): def __init__(self, tv: TV): self.tv = tv self.previous_state = None def execute(self): self.previous_state = self.tv.is_on self.tv.turn_on() def undo(self): if self.previous_state == False: self.tv.turn_off() print("[Command] Undone: TV turned back OFF")

Step 4: Create Turn Off Command

# turn_off_command.py from command_interface import Command from tv import TV class TurnOffCommand(Command): def __init__(self, tv: TV): self.tv = tv self.previous_state = None def execute(self): self.previous_state = self.tv.is_on self.tv.turn_off() def undo(self): if self.previous_state == True: self.tv.turn_on() print("[Command] Undone: TV turned back ON")

Step 5: Create Volume Up Command

# volume_up_command.py from command_interface import Command from tv import TV class VolumeUpCommand(Command): def __init__(self, tv: TV): self.tv = tv self.previous_volume = None def execute(self): self.previous_volume = self.tv.volume self.tv.increase_volume() def undo(self): if self.previous_volume is not None: self.tv.volume = self.previous_volume print(f"[Command] Undone: Volume restored to {self.previous_volume}")

Step 6: Create Volume Down Command

# volume_down_command.py from command_interface import Command from tv import TV class VolumeDownCommand(Command): def __init__(self, tv: TV): self.tv = tv self.previous_volume = None def execute(self): self.previous_volume = self.tv.volume self.tv.decrease_volume() def undo(self): if self.previous_volume is not None: self.tv.volume = self.previous_volume print(f"[Command] Undone: Volume restored to {self.previous_volume}")

Step 7: Create Remote Control (Invoker)

# remote_control.py from command_interface import Command class RemoteControl: def __init__(self): self.last_command = None # Store last command for undo def press_button(self, command: Command): """Press a button (execute a command)""" command.execute() self.last_command = command # Save for undo def press_undo(self): """Undo the last command""" if self.last_command: self.last_command.undo() self.last_command = None else: print("[Remote] No command to undo")

Step 8: Use the Command Pattern (Client Code)

# main.py from tv import TV from remote_control import RemoteControl from turn_on_command import TurnOnCommand from turn_off_command import TurnOffCommand from volume_up_command import VolumeUpCommand from volume_down_command import VolumeDownCommand # Create TV and remote tv = TV() remote = RemoteControl() print("=== Initial State ===") print(tv.get_status()) # Turn TV on print("\n=== Pressing Power Button (ON) ===") remote.press_button(TurnOnCommand(tv)) print(tv.get_status()) # Increase volume print("\n=== Pressing Volume Up ===") remote.press_button(VolumeUpCommand(tv)) remote.press_button(VolumeUpCommand(tv)) print(tv.get_status()) # Undo last action (volume up) print("\n=== Pressing Undo ===") remote.press_undo() print(tv.get_status()) # Undo again (volume up) print("\n=== Pressing Undo Again ===") remote.press_undo() print(tv.get_status()) # Turn TV off print("\n=== Pressing Power Button (OFF) ===") remote.press_button(TurnOffCommand(tv)) print(tv.get_status()) # Undo turning off print("\n=== Pressing Undo ===") remote.press_undo() print(tv.get_status())

Output:

=== Initial State ===
TV is OFF, Volume: 10

=== Pressing Power Button (ON) ===
[TV] TV turned ON
TV is ON, Volume: 10

=== Pressing Volume Up ===
[TV] Volume increased to 11
[TV] Volume increased to 12
TV is ON, Volume: 12

=== Pressing Undo ===
[Command] Undone: Volume restored to 11
TV is ON, Volume: 11

=== Pressing Undo Again ===
[Command] Undone: Volume restored to 10
TV is ON, Volume: 10

=== Pressing Power Button (OFF) ===
[TV] TV turned OFF
TV is OFF, Volume: 10

=== Pressing Undo ===
[Command] Undone: TV turned back ON
TV is ON, Volume: 10

✅ Benefits

1. Separation:

  • The remote (button presser) doesn't need to know how the TV works
  • Commands can be saved and used later, anywhere

2. Easy Undo:

  • Each command remembers what it did, so it can undo itself
  • Adding undo is simple - just call the undo method

3. Save for Later:

  • Commands can be saved in a list and done all at once
  • Useful when you want to do multiple things together

4. Keep Track:

  • You can save a list of all commands that were done
  • Useful for seeing what happened or fixing problems

🎯 Key Points

  • Command Pattern turns each action into an object
  • Commands can be saved, put in a queue, and undone
  • The button (invoker) doesn't need to know how things work
  • Easy to add new commands without changing old code
  • Perfect when you need undo/redo features

🌟 Quick Summary

  • Problem: You want to separate the button that triggers an action from the thing that does the action, and you want to be able to undo actions
  • Solution: Turn each action into a command object that knows how to do the action and how to undo it
  • Benefit: Easy undo/redo, can save commands for later, can keep track of what was done
  • Example: Remote control - each button press creates a command object that can do the action or undo it

🎉 End

The Command Pattern helps you create systems where actions can be saved, done later, and undone easily. It's perfect when you need undo/redo features, like in text editors or remote controls!