The Command pattern is a behavioral pattern that allows you to encapsulate a request as an object, thereby allowing you to parameterize clients with different requests, queue or log requests, and support undoable operations.
The main idea behind the Command pattern is to separate the object that invokes the operation from the object that knows how to perform the operation. This separation allows you to decouple the client that sends the request from the receiver that performs the request, and also allows you to implement the request as an object that can be passed as a parameter, stored, or manipulated like any other object.
Here’s an example of the Command pattern in Python:
# Command interface
class Command:
def execute(self):
pass
# Concrete command classes
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
class Light:
def turn_on(self):
print("Light is on")
def turn_off(self):
print("Light is off")
# Invoker class
class Switch:
def __init__(self, on_command, off_command):
self._on_command = on_command
self._off_command = off_command
def turn_on(self):
self._on_command.execute()
def turn_off(self):
self._off_command.execute()
# Client code
def client_code(switch: Switch):
switch.turn_on()
switch.turn_off()
# Usage
light = Light()
light_on_command = LightOnCommand(light)
light_off_command = LightOffCommand(light)
switch = Switch(light_on_command, light_off_command)
client_code(switch)
In this example, we have a Command
interface that defines the common interface for both the LightOnCommand
and LightOffCommand
classes. The LightOnCommand
and LightOffCommand
classes represent the concrete commands that perform the “turn on” and “turn off” operations, respectively.
The Light
class represents the receiver that knows how to perform the “turn on” and “turn off” operations.
The Switch
class represents the invoker that knows how to execute the commands. The Switch
class has an on_command
attribute and an off_command
attribute that represent the commands to execute when the switch is turned on and off, respectively.
When we create a Switch
object with the LightOnCommand
and LightOffCommand
objects as parameters, and pass the Switch
object to the client_code
function, we get the output:
Light is on
Light is off
As you can see, the Switch
object executes the LightOnCommand
object when it is turned on, and executes the LightOffCommand
object when it is turned off. This example demonstrates how the Command pattern can be used to encapsulate requests as objects, and decouple the object that invokes the operation from the object that knows how to perform the operation.
Pros of the Command pattern
- It decouples the sender of a request from the object that performs the action, allowing for greater flexibility and extensibility.
- It supports the implementation of undo/redo functionality by providing methods to execute and undo commands.
- It enables the logging and queuing of requests, allowing for features like transactional behavior or command history.
Cons of the Command pattern
- The pattern can introduce a large number of command classes, which may increase code complexity.
- It may not be suitable for commands that require direct communication or complex interactions between the sender and receiver.
Related design patterns
- Observer: The Command pattern is often used in conjunction with the Observer pattern, where the invoker sends commands to observers, and the observers carry out the commands. This allows for loosely coupled communication between the invoker and observers.
- Memento: The Command pattern can be used with the Memento pattern to support undo/redo functionality. The Memento pattern can capture the state of the receiver before executing a command and restore it if needed for undoing the command.