Patrón de Comando en Python
El patrón de comando es un patrón de diseño de comportamiento que encapsula una solicitud en un objeto, lo que te permite parametrizar clientes con diferentes solicitudes. En términos simples,El patrón de comando envuelve operaciones (como abrir archivos, guardar datos) en objetos independientes, lo que te permite manejar operaciones como si fueran datos.
Analogía en la vida cotidiana
Imagina el proceso de pedido en un restaurante:
- Cliente(el cliente) no necesita saber cómo cocina el chef
- Camarero(el invocador) recibe el pedido pero no cocina personalmente
- Pedido(el objeto de comando) contiene la información específica del plato
- Chef(el receptor) ejecuta las operaciones de cocina específicas según el pedido
Este patrón con una división clara del trabajo es la manifestación del patrón de comando en la vida real.
¿Por qué se necesita el patrón de comando?
Limitaciones del enfoque tradicional
En la programación tradicional, generalmente llamamos directamente a los métodos de un objeto:
Ejemplo
def turn_on(self):
print("La luz está encendida")
def turn_off(self):
print("La luz está apagada")
# Llamada directa
light = Light()
light.turn_on()
light.turn_off()
Esta forma tiene varios problemas:
- Acoplamiento fuerte: el invocador necesita conocer la interfaz específica del receptor
- Difícil de extender: agregar nuevas funciones requiere modificar el código existente
- No admite deshacer/rehacer: después de ejecutar la operación, no se puede revertir
Ventajas del patrón de comando

El patrón de comando resuelve los problemas anteriores al introducir una capa intermedia (el objeto de comando) y ofrece:
- Desacoplamiento: no hay dependencia directa entre el invocador y el receptor
- Extensibilidad: es fácil agregar nuevos comandos
- Soporte para deshacer/rehacer: se puede registrar el historial de operaciones
- Soporte de colas: se pueden poner los comandos en una cola para ejecutarlos de forma diferida
Componentes principales del patrón de comando
Estructura básica
El patrón de comando incluye cuatro roles principales:
| Rol | Responsabilidad | Ejemplo |
|---|---|---|
| Command(Interfaz de comando) | Declara la interfaz para ejecutar operaciones | execute(), undo() |
| ConcreteCommand(Comando concreto) | Implementa la interfaz de comando y se vincula al receptor | LightOnCommand, LightOffCommand |
| Receiver(Receptor) | Sabe cómo ejecutar la operación | Light, TV |
| Invoker(Invocador) | Almacena y ejecuta comandos | RemoteControl |
Ejemplo de código completo
Implementación básica
Entendamos el patrón de comando mediante un ejemplo de control de luces en un hogar inteligente:
Ejemplo
from typing import List
# 1. Interfaz de comando
class Command(ABC):
@abstractmethod
def execute(self):
pass
@abstractmethod
def undo(self):
pass
# 2. Receptor - Dispositivo de luz
class Light:
def turn_on(self):
print("💡 Luz encendida")
def turn_off(self):
print("💡 Luz apagada")
# 3. Comando concreto - Comando para encender la luz
class LightOnCommand(Command):
def __init__(self, light: Light):
self.light = light
def execute(self):
self.light.turn_on()
def undo(self):
self.light.turn_off()
# 4. Comando concreto - Comando para apagar la luz
class LightOffCommand(Command):
def __init__(self, light: Light):
self.light = light
def execute(self):
self.light.turn_off()
def undo(self):
self.light.turn_on()
# 5. Invocador - Control remoto
class RemoteControl:
def __init__(self):
self.command = None
self.history: List[Command] = []
def set_command(self, command: Command):
self.command = command
def press_button(self):
if self.command:
self.command.execute()
self.history.append(self.command)
def press_undo(self):
if self.history:
last_command = self.history.pop()
last_command.undo()
# Código del cliente
if __name__ == "__main__":
# Crear dispositivo
living_room_light = Light()
# Crear comando
light_on = LightOnCommand(living_room_light)
light_off = LightOffCommand(living_room_light)
# Crear control remoto
remote = RemoteControl()
# Probar encendido de la luz
print("=== Prueba de encendido ===")
remote.set_command(light_on)
remote.press_button()
# Probar apagado de la luz
print("\n=== Prueba de apagado ===")
remote.set_command(light_off)
remote.press_button()
# Probar deshacer
print("\n=== Prueba de operación de deshacer ===)
remote.press_undo() # Deshacer apagado de la luz, debería encenderse
remote.press_undo() # Deshacer encendido de la luz, debería apagarse
Resultado de la ejecución:

Ejemplo de aplicación avanzada
Control remoto universal compatible con múltiples dispositivos
Ampliemos el ejemplo anterior para crear un control remoto universal que admita múltiples dispositivos:
Ejemplo
class TV:
def __init__(self, location: str):
self.location = location
self.is_on = False
self.volume = 50
def turn_on(self):
self.is_on = True
print(f"📺 {self.location} Televisor encendido")
def turn_off(self):
self.is_on = False
print(f"📺 {self.location} Televisor apagado")
def set_volume(self, volume: int):
self.volume = volume
print(f"📺 {self.location} Volumen del televisor establecido en {volume}")
# Comandos relacionados con el televisor
class TVOnCommand(Command):
def __init__(self, tv: TV):
self.tv = tv
self.previous_volume = 50
def execute(self):
self.previous_volume = self.tv.volume
self.tv.turn_on()
def undo(self):
self.tv.turn_off()
self.tv.volume = self.previous_volume
class TVVolumeUpCommand(Command):
def __init__(self, tv: TV):
self.tv = tv
self.previous_volume = 50
def execute(self):
self.previous_volume = self.tv.volume
self.tv.set_volume(min(100, self.tv.volume + 10))
def undo(self):
self.tv.set_volume(self.previous_volume)
# Macrocomando - Ejecutar múltiples comandos con un solo clic
class MacroCommand(Command):
def __init__(self, commands: List[Command]):
self.commands = commands
def execute(self):
for command in self.commands:
command.execute()
def undo(self):
# Deshacer en orden inverso
for command in reversed(self.commands):
command.undo()
# Control remoto mejorado
class AdvancedRemoteControl:
def __init__(self, slot_count: int = 4):
self.on_commands: List[Command] = [None] * slot_count
self.off_commands: List[Command] = [None] * slot_count
self.history: List[Command] = []
def set_command(self, slot: int, on_command: Command, off_command: Command):
self.on_commands[slot] = on_command
self.off_commands[slot] = off_command
def press_on_button(self, slot: int):
if self.on_commands[slot]:
self.on_commands[slot].execute()
self.history.append(self.on_commands[slot])
def press_off_button(self, slot: int):
if self.off_commands[slot]:
self.off_commands[slot].execute()
self.history.append(self.off_commands[slot])
def press_undo(self):
if self.history:
last_command = self.history.pop()
last_command.undo()
# Probar control remoto avanzado
def test_advanced_remote():
print("=== Prueba del control remoto universal ===")
# Crear dispositivo
living_room_light = Light()
bedroom_tv = TV("Dormitorio")
# Crear comando
light_on = LightOnCommand(living_room_light)
light_off = LightOffCommand(living_room_light)
tv_on = TVOnCommand(bedroom_tv)
tv_volume_up = TVVolumeUpCommand(bedroom_tv)
# Crear macrocomando - Modo cine
cinema_mode = MacroCommand([light_off, tv_on, tv_volume_up])
# Configurar control remoto
remote = AdvancedRemoteControl()
remote.set_command(0, light_on, light_off) # Ranura 0: Control de luz
remote.set_command(1, tv_on, TVOnCommand(bedroom_tv)) # Ranura 1: Control del televisor
remote.set_command(2, cinema_mode, light_on) # Ranura 2: Modo cine
# Probar varias funciones
print("\n1. Encender la luz:)
remote.press_on_button(0)
print("\n2. Activar modo cine:)
remote.press_on_button(2)
print("\n3. Deshacer operación:)
remote.press_undo()
if __name__ == "__main__":
test_advanced_remote()
Parámetros y opciones del patrón de comando
Extensión de la interfaz de comando
En aplicaciones reales, la interfaz de comando se puede extender con más funciones según sea necesario:
Ejemplo
@abstractmethod
def execute(self):
"""Ejecutar comando"""
pass
@abstractmethod
def undo(self):
"""Deshacer comando"""
pass
@abstractmethod
def redo(self):
"""Rehacer comando"""
pass
@abstractmethod
def can_execute(self) -> bool:
"""Verificar si el comando puede ejecutarse"""
pass
@abstractmethod
def get_description(self) -> str:
"""Obtener la descripción del comando"""
pass
Cola de comandos y ejecución diferida
El patrón de comando admite de forma natural colas de comandos, lo cual es muy útil en muchos escenarios:
Ejemplo
def __init__(self):
self.queue: List[Command] = []
def add_command(self, command: Command):
self.queue.append(command)
def process_commands(self):
while self.queue:
command = self.queue.pop(0)
if command.can_execute():
command.execute()
def clear(self):
self.queue.clear()
Ejercicios prácticos
Ejercicio 1: Implementar un sistema de control de aire acondicionado
Intenta implementar el patrón de comando para un sistema de control de aire acondicionado:
Ejemplo
class AirConditioner:
def __init__(self):
self.temperature = 26
self.is_on = False
def turn_on(self):
# Implementar encendido del aire acondicionado
pass
def turn_off(self):
# Implementar apagado del aire acondicionado
pass
def set_temperature(self, temp: int):
# Implementar ajuste de temperatura
pass
# Implementar clases de comando relacionadas con el aire acondicionado
# AirConditionerOnCommand
# AirConditionerOffCommand
# TemperatureUpCommand
# TemperatureDownCommand
Ejercicio 2: Implementar la función de deshacer de un editor de texto
Crea un editor de texto simple que admita ingresar texto y operaciones de deshacer:
Ejemplo
def __init__(self):
self.content = ""
def add_text(self, text: str):
# Implementar agregar texto
pass
def delete_text(self, length: int):
# Implementar eliminar texto
pass
# Implementar comandos de edición de texto
# AddTextCommand
# DeleteTextCommand
Preguntas frecuentes
Q: ¿Para qué escenarios es adecuado el patrón de comando?
A:El patrón de comando es especialmente adecuado para los siguientes escenarios:
- Necesidad de parametrizar operaciones (por ejemplo, vincular diferentes funciones a botones)
- Necesidad de admitir funciones de deshacer/rehacer
- Necesidad de poner operaciones en una cola para ejecutarlas de forma diferida
- Necesidad de llevar un registro de operaciones
- Necesidad de admitir operaciones transaccionales
Q: ¿Cuáles son las desventajas del patrón de comando?
A:Las principales desventajas incluyen:
- Puede crear una gran cantidad de clases de comando
- Aumenta la complejidad del código
- Puede resultar demasiado engorroso para operaciones simples
Q: ¿Cómo decidir si usar el patrón de comando?
A:Considera los siguientes puntos:
- Si solo necesitas llamadas simples a métodos, no uses el patrón de comando
- Si necesitas funciones de deshacer/rehacer, se recomienda encarecidamente su uso
- Si el sistema necesita un alto desacoplamiento, el patrón de comando es una excelente opción
- Si las operaciones necesitan colas o registro de logs, el patrón de comando es muy adecuado
Resumen
El patrón de comando encapsula operaciones como objetos, desacoplando al invocador del receptor y proporcionando una gran capacidad de extensión. Aunque añade cierta complejidad al código, en escenarios que requieren deshacer/rehacer, colas de operaciones, registro de registros, etc., el patrón de comando es un patrón de diseño indispensable.
Puntos clave:
- El patrón de comando separa el "qué hacer" del "quién lo hace"
- Soporta funciones avanzadas como deshacer, rehacer y colas
- Fácil de extender con nuevos comandos
- Ampliamente utilizado en aplicaciones GUI, sistemas transaccionales y desarrollo de juegos