How to Implement the Command Pattern in Python
The Command Pattern is a behavioral design pattern that turns a request into a standalone object that contains all information about the request. This allows you to parameterize clients with different requests, queue or log requests, and support undoable operations. Let us delve into understanding how the Command Pattern can be used in Python.
1. Understanding the Command Pattern
The Command Pattern is a behavioral design pattern that encapsulates a request as an object, thereby allowing you to parameterize clients with different requests, queue or log them, and support undoable operations. Instead of calling methods directly on objects, the request is wrapped inside a command object. This object contains all the information needed to perform the action, including the method to call and the receiver of the method. This design promotes loose coupling between the sender (Invoker) and the receiver (the object that performs the action), making systems more flexible, extensible, and easier to maintain.
1.1 Core Concepts and Structure
- Command: Declares the interface (typically
execute()) for executing an operation. It may also define additional methods likeundo()for reversible actions. - ConcreteCommand: Implements the Command interface and binds a receiver object with an action. It translates the execute request into one or more calls on the receiver.
- Receiver: The object that contains the actual business logic. It knows how to perform the work associated with the request.
- Invoker: The object responsible for triggering the command. It does not know how the command is executed; it simply calls the
execute()method. - Client: Responsible for creating and configuring command objects. It assigns the receiver to the command and passes the command to the invoker.
1.2 When Should You Use the Command Pattern?
- Decoupling sender and receiver: When you want to separate the object that invokes an operation from the one that performs it.
- Undo/Redo functionality: When you need to support reversible operations (e.g., text editors, drawing tools). Commands can store state and reverse actions using an
undo()method. - Queueing and scheduling tasks: Useful in job queues, background processing, and task schedulers where commands can be stored and executed later.
- Logging and auditing: Commands can be logged for replay, debugging, or auditing purposes.
- Macro commands (batch processing): Multiple commands can be combined into a single composite command to execute a sequence of operations.
- Callback or event-driven systems: When replacing function callbacks with object-oriented command structures for better extensibility.
1.3 Advantages of the Command Pattern
- Promotes loose coupling between components
- Easy to add new commands without modifying existing code (Open/Closed Principle)
- Supports undo/redo operations
- Enables command queuing and logging
- Improves code readability and maintainability
1.4 Limitations and Trade-offs
- Can increase the number of classes in the system
- May introduce additional complexity for simple use cases
- Overhead of creating command objects for every request
1.5 Real-World Use Cases
- Remote controls: Each button represents a command (turn on/off devices)
- Text editors: Commands for typing, deleting, and undoing actions
- GUI frameworks: Menu actions and button clicks mapped to commands
- Task queues: Background job processing systems (e.g., Celery in Python)
2. Step-by-Step Implementation in Python
2.1 Defining the Command Interface
The following code defines the abstract command interface that all concrete commands must implement.
# file: command.py
from abc import ABC, abstractmethod
class Command(ABC):
@abstractmethod
def execute(self):
pass
2.1.1 Code Explanation
The above code defines an abstract base class named Command using Python’s abc (Abstract Base Classes) module, which is designed to enforce a common interface across all concrete command implementations in the Command Pattern. By inheriting from ABC, the Command class becomes an abstract class, meaning it cannot be instantiated directly and is intended to be subclassed. The @abstractmethod decorator is applied to the execute() method, ensuring that any subclass of Command must provide its own implementation of this method. The execute() method represents the core operation that encapsulates a request, and although it contains only a pass statement here (indicating no default behavior), it acts as a contract that enforces consistency across all command objects. This structure is crucial in the Command Pattern because it allows the invoker to call execute() on any command object without needing to know the details of how the request is processed, thereby achieving loose coupling between the sender and the receiver.
2.2 Implementing the Receiver
The following code defines the receiver class that contains the actual business logic.
# file: light.py
class Light:
def turn_on(self):
print("Light is ON")
def turn_off(self):
print("Light is OFF")
2.2.1 Code Explanation
The above code defines a Receiver class named Light, which represents the object that performs the actual work in the Command Pattern. It contains two methods: turn_on() and turn_off(), each encapsulating a specific piece of business logic—in this case, printing the state of the light. These methods simulate real-world actions (such as controlling a physical light device), but in practice, they could include more complex operations like interacting with hardware or external systems. The Light class itself is unaware of the Command Pattern or how its methods are invoked; it simply exposes functionality. This separation of concerns is important because command objects (like LightOnCommand and LightOffCommand) will call these methods without the invoker needing to know any details about the Light class, thereby maintaining loose coupling and making the system more modular and extensible.
2.3 Creating Concrete Commands
The following code implements concrete command classes that bind actions to the receiver.
# file: light_commands.py
from command import 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()
2.3.1 Code Explanation
The above code defines two Concrete Command classes—LightOnCommand and LightOffCommand—which implement the abstract Command interface and provide specific actions to be executed. Each class takes an instance of the Light receiver as a dependency through its constructor (__init__) and stores it as an instance variable. This establishes a connection between the command and the receiver it will act upon. The execute() method in each class overrides the abstract method defined in the base Command class and delegates the actual work to the receiver by calling the appropriate method—turn_on() for LightOnCommand and turn_off() for LightOffCommand. This encapsulation of a request as an object allows the invoker to trigger these commands without knowing any details about the Light class or the specific operations being performed, thereby achieving loose coupling and enabling flexibility such as easily adding new commands or changing behavior at runtime.
2.4 Implementing the Invoker
The following code defines the invoker that triggers command execution.
# file: remote_control.py
class RemoteControl:
def __init__(self):
self.command = None
def set_command(self, command):
self.command = command
def press_button(self):
if self.command:
self.command.execute()
2.4.1 Code Explanation
The above code defines the Invoker class named RemoteControl, which is responsible for triggering command execution without knowing the details of how the command is implemented. The class maintains a reference to a command object through the instance variable self.command, which is initially set to None. The set_command() method allows the client to assign any concrete command (such as LightOnCommand or LightOffCommand) to the invoker dynamically, enabling flexible behavior at runtime. The press_button() method acts as the trigger mechanism; it checks whether a command has been set and, if so, calls its execute() method. Importantly, the RemoteControl class does not know anything about the receiver (e.g., Light) or the specific action being performed—it simply invokes the command interface. This abstraction is a key benefit of the Command Pattern, as it decouples the invoker from the actual business logic, making the system more modular, extensible, and easy to maintain.
2.5 Client Code
The following code shows how all components are wired together and executed.
# file: main.py
from light import Light
from light_commands import LightOnCommand, LightOffCommand
from remote_control import RemoteControl
if __name__ == "__main__":
# Receiver
light = Light()
# Commands
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)
# Invoker
remote = RemoteControl()
# Turn ON
remote.set_command(light_on)
remote.press_button()
# Turn OFF
remote.set_command(light_off)
remote.press_button()
2.5.1 Code Explanation
The above code represents the Client section, where all components of the Command Pattern are instantiated and wired together to demonstrate how the pattern works in practice. The execution starts with the creation of the Light object, which acts as the receiver containing the actual business logic. Next, two concrete command objects—LightOnCommand and LightOffCommand—are created, each receiving the light instance so they can delegate actions to it. Then, an instance of the RemoteControl (the invoker) is created, which will be used to trigger commands. The client dynamically assigns commands to the invoker using the set_command() method, first setting the light_on command and calling press_button() to execute it, resulting in turning the light on. The process is then repeated with the light_off command to turn the light off. This flow clearly demonstrates how the client configures the system and how the invoker remains completely unaware of the underlying implementation details, relying solely on the command interface to execute actions, thereby achieving loose coupling and runtime flexibility.
2.6 Running the Application and Output
The following output shows the result of executing the command pattern implementation.
Light is ON Light is OFF
The above output demonstrates the result of executing the Command Pattern implementation, where the invoker (RemoteControl) triggers different commands assigned to it at runtime. When the light_on command is set and the button is pressed, the execute() method of LightOnCommand is invoked, which internally calls the turn_on() method of the Light receiver, resulting in the output "Light is ON". Similarly, when the light_off command is assigned and executed, it triggers the turn_off() method of the receiver, producing "Light is OFF". This output validates how the Command Pattern enables dynamic behavior by allowing different commands to be executed through the same invoker interface, without the invoker needing to know any details about the underlying actions or receiver logic.
3. Conclusion
The Command Pattern is a powerful way to decouple objects and make systems more flexible and extensible. It is especially useful when implementing features like undo/redo, task queues, and macro commands. By encapsulating actions into command objects, you can easily add new commands without modifying existing code, making your system more maintainable and scalable.



