Understanding the Command Pattern with Python Examples
This article explains the Command design pattern, describes its key participants and advantages, and provides five Python examples demonstrating command encapsulation, undo/redo functionality, file operations, shopping‑cart management, and menu handling to illustrate how the pattern decouples request senders from receivers.
The Command pattern is a behavioral design pattern that encapsulates a request as an object, allowing parameterization of clients with different requests and decoupling the request from its receiver.
Key roles in the Command pattern: Command (interface with execute ), ConcreteCommand (implements the command), Receiver (performs the actual operation), Invoker (calls the command), and Client (creates and configures commands).
Advantages: Decouples invoker and receiver, improves extensibility by adding new commands without changing existing code, and enables undo/redo operations by storing command objects.
Example 1: Controlling a light switch
from abc import ABC, abstractmethod
# Command
class Command(ABC):
@abstractmethod
def execute(self):
pass
# Concrete Command
class LightOnCommand(Command):
def __init__(self, light):
self.light = light
def execute(self):
self.light.turn_on()
class LightOffCommand(Command):
def __init__(self, light):
self.light = light
def execute(self):
self.light.turn_off()
# Receiver
class Light:
def turn_on(self):
print("Light is on.")
def turn_off(self):
print("Light is off.")
# Invoker
class RemoteControl:
def __init__(self):
self.commands = {}
def set_command(self, slot, command):
self.commands[slot] = command
def press_button(self, slot):
if slot in self.commands:
self.commands[slot].execute()
# Client
light = Light()
light_on_command = LightOnCommand(light)
light_off_command = LightOffCommand(light)
remote_control = RemoteControl()
remote_control.set_command(1, light_on_command)
remote_control.set_command(2, light_off_command)
remote_control.press_button(1) # Turns on the light
remote_control.press_button(2) # Turns off the lightExample 2: Simple undo operation
from abc import ABC, abstractmethod
# Command
class Command(ABC):
@abstractmethod
def execute(self):
pass
@abstractmethod
def undo(self):
pass
# Concrete Command
class AddCommand(Command):
def __init__(self, calculator, value):
self.calculator = calculator
self.value = value
def execute(self):
self.calculator.add(self.value)
def undo(self):
self.calculator.subtract(self.value)
# Receiver
class Calculator:
def __init__(self):
self.result = 0
def add(self, value):
self.result += value
print(f"Result: {self.result}")
def subtract(self, value):
self.result -= value
print(f"Result: {self.result}")
# Invoker
class Invoker:
def __init__(self):
self.commands = []
def execute_command(self, command):
command.execute()
self.commands.append(command)
def undo_last_command(self):
if self.commands:
last_command = self.commands.pop()
last_command.undo()
# Client
calculator = Calculator()
add_command = AddCommand(calculator, 5)
invoker = Invoker()
invoker.execute_command(add_command) # Result: 5
invoker.execute_command(AddCommand(calculator, 3)) # Result: 8
invoker.undo_last_command() # Result: 5Example 3: File operations
from abc import ABC, abstractmethod
# Command
class Command(ABC):
@abstractmethod
def execute(self):
pass
# Concrete Command
class CreateFileCommand(Command):
def __init__(self, filename):
self.filename = filename
def execute(self):
with open(self.filename, 'w') as f:
print(f"Created file: {self.filename}")
class DeleteFileCommand(Command):
def __init__(self, filename):
self.filename = filename
def execute(self):
import os
os.remove(self.filename)
print(f"Deleted file: {self.filename}")
# Receiver
class FileHandler:
def handle(self, command):
command.execute()
# Client
file_handler = FileHandler()
create_file_command = CreateFileCommand("example.txt")
delete_file_command = DeleteFileCommand("example.txt")
file_handler.handle(create_file_command) # Created file: example.txt
file_handler.handle(delete_file_command) # Deleted file: example.txtExample 4: Undo/redo with a shopping cart
from abc import ABC, abstractmethod
# Command
class Command(ABC):
@abstractmethod
def execute(self):
pass
@abstractmethod
def undo(self):
pass
# Concrete Command
class AddItemCommand(Command):
def __init__(self, items, item):
self.items = items
self.item = item
def execute(self):
self.items.append(self.item)
print(f"Added item: {self.item}")
def undo(self):
self.items.remove(self.item)
print(f"Removed item: {self.item}")
# Receiver
class ShoppingCart:
def __init__(self):
self.items = []
def display_items(self):
print("Shopping Cart:")
for item in self.items:
print(item)
print()
# Invoker
class Invoker:
def __init__(self):
self.commands = []
def execute_command(self, command):
command.execute()
self.commands.append(command)
def undo_last_command(self):
if self.commands:
last_command = self.commands.pop()
last_command.undo()
# Client
shopping_cart = ShoppingCart()
add_item_command = AddItemCommand(shopping_cart.items, "Item 1")
invoker = Invoker()
invoker.execute_command(add_item_command) # Added item: Item 1
invoker.execute_command(AddItemCommand(shopping_cart.items, "Item 2")) # Added item: Item 2
shopping_cart.display_items()
invoker.undo_last_command() # Removed item: Item 2
shopping_cart.display_items()Example 5: Menu operations
from abc import ABC, abstractmethod
# Command
class Command(ABC):
@abstractmethod
def execute(self):
pass
# Concrete Command
class MenuItem1Command(Command):
def execute(self):
print("Executing Menu Item 1 command")
class MenuItem2Command(Command):
def execute(self):
print("Executing Menu Item 2 command")
# Receiver
class Menu:
def display(self):
print("Menu:")
print("1. Menu Item 1")
print("2. Menu Item 2")
print()
# Invoker
class Invoker:
def __init__(self):
self.commands = {}
def register_command(self, key, command):
self.commands[key] = command
def execute_command(self, key):
if key in self.commands:
self.commands[key].execute()
else:
print("Invalid command!")
# Client
menu = Menu()
menu_item1_command = MenuItem1Command()
menu_item2_command = MenuItem2Command()
invoker = Invoker()
invoker.register_command(1, menu_item1_command)
invoker.register_command(2, menu_item2_command)
menu.display()
invoker.execute_command(1) # Executing Menu Item 1 command
invoker.execute_command(2) # Executing Menu Item 2 command
invoker.execute_command(3) # Invalid command!These examples demonstrate how the Command pattern can be applied to various scenarios such as toggling devices, implementing undo/redo, performing file operations, managing a shopping cart, and handling menu selections, providing flexible and extensible code structures.
Test Development Learning Exchange
Test Development Learning Exchange
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.