Lección 8.6: Patron de diseño Command
Patrón de Diseño Command en Python
Introducción
¡Hola a todos! Soy Yuscu Mejía y en esta lección vamos a explorar el concepto del patrón de diseño Command en Python. El patrón Command convierte las solicitudes en objetos, lo que permite parametrizar otros objetos con diferentes solicitudes, encolar o registrar solicitudes, y soportar operaciones que se pueden deshacer.
Características
El patrón Command en Python tiene las siguientes características:
- Encapsula una solicitud como un objeto, permitiendo parametrizar clientes con diferentes solicitudes.
- Permite encolar y registrar solicitudes.
- Facilita la implementación de operaciones que se pueden deshacer.
Propósito
El patrón de diseño Command convierte las solicitudes en objetos, lo que permite parametrizar otros objetos con diferentes solicitudes, encolar o registrar solicitudes, y soportar operaciones que se pueden deshacer.
Problema
El problema que resuelve el patrón Command es cómo desacoplar el objeto que invoca una operación del objeto que realmente la ejecuta, permitiendo parametrizar y gestionar solicitudes de manera flexible.
Solución
La solución propuesta por el patrón Command es encapsular una solicitud en un objeto, lo que permite parametrizar otros objetos con diferentes solicitudes, encolar o registrar solicitudes, y soportar operaciones que se pueden deshacer.
Analogía en el Mundo Real
Una analogía del mundo real sería un menú en un restaurante. Un cliente selecciona un ítem del menú, y el camarero (invocador) toma la orden y la envía a la cocina (receptor), donde se prepara el platillo.
Estructura
La estructura del patrón Command incluye los siguientes componentes:
- Command: Declara una interfaz para ejecutar una operación.
- ConcreteCommand: Define un enlace entre un objeto receptor y una acción. Implementa Execute invocando la operación correspondiente en el receptor.
- Client: Crea un objeto ConcreteCommand y lo configura con el receptor.
- Invoker: Pide a un objeto Command que ejecute la solicitud.
- Receiver: Sabe cómo realizar las operaciones asociadas a llevar a cabo una solicitud. Cualquier clase puede servir como un receptor.

Aplicabilidad
El patrón Command es aplicable cuando:
- Se desea parametrizar objetos con operaciones.
- Se necesita especificar, encolar y ejecutar solicitudes en diferentes momentos.
- Se requiere implementar operaciones que se pueden deshacer.
Pros y Contras
- Pros:
- Permite el desacoplamiento entre el objeto que invoca la operación y el objeto que la ejecuta.
- Facilita la implementación de operaciones que se pueden deshacer y rehacer.
- Permite encolar y registrar solicitudes.
- Contras:
- Puede introducir complejidad adicional debido a la creación de múltiples clases y objetos.
Vamos a ver el uso del Patrón Command
A continuación, un ejemplo de cómo utilizar el patrón Command en Python, presentado por Yuscu Mejía:
- Patrón Command:
# Definir la interfaz Command
from abc import ABC, abstractmethod
class Command(ABC):
@abstractmethod
def ejecutar(self):
pass
# Definir el receptor
class Luz:
def encender(self):
print("La luz está encendida.")
def apagar(self):
print("La luz está apagada.")
class Ventilador:
def encender(self):
print("El ventilador está encendido.")
def apagar(self):
print("El ventilador está apagado.")
# Definir los comandos concretos
class EncenderLuzCommand(Command):
def __init__(self, luz: Luz):
self.luz = luz
def ejecutar(self):
self.luz.encender()
class ApagarLuzCommand(Command):
def __init__(self, luz: Luz):
self.luz = luz
def ejecutar(self):
self.luz.apagar()
class EncenderVentiladorCommand(Command):
def __init__(self, ventilador: Ventilador):
self.ventilador = ventilador
def ejecutar(self):
self.ventilador.encender()
class ApagarVentiladorCommand(Command):
def __init__(self, ventilador: Ventilador):
self.ventilador = ventilador
def ejecutar(self):
self.ventilador.apagar()
# Definir el invocador
class ControlRemoto:
def __init__(self):
self.comandos = {}
def asignar_comando(self, boton, comando: Command):
self.comandos[boton] = comando
def presionar_boton(self, boton):
if boton in self.comandos:
self.comandos[boton].ejecutar()
else:
print(f"No hay comando asignado al botón {boton}")
# Ejemplo de uso
if __name__ == "__main__":
# Crear receptores
luz = Luz()
ventilador = Ventilador()
# Crear comandos
encender_luz = EncenderLuzCommand(luz)
apagar_luz = ApagarLuzCommand(luz)
encender_ventilador = EncenderVentiladorCommand(ventilador)
apagar_ventilador = ApagarVentiladorCommand(ventilador)
# Configurar el control remoto
control_remoto = ControlRemoto()
control_remoto.asignar_comando("boton1", encender_luz)
control_remoto.asignar_comando("boton2", apagar_luz)
control_remoto.asignar_comando("boton3", encender_ventilador)
control_remoto.asignar_comando("boton4", apagar_ventilador)
# Usar el control remoto
control_remoto.presionar_boton("boton1") # Imprime: La luz está encendida.
control_remoto.presionar_boton("boton2") # Imprime: La luz está apagada.
control_remoto.presionar_boton("boton3") # Imprime: El ventilador está encendido.
control_remoto.presionar_boton("boton4") # Imprime: El ventilador está apagado.
Vamos a ver el uso del Patrón Command
A continuación, otro ejemplo de cómo utilizar el patrón Command en Python pero agregando la funcion deshacer, presentado por Yuscu Mejía:
- Patrón Command:
# Definir la interfaz Command
from abc import ABC, abstractmethod
class Command(ABC):
@abstractmethod
def ejecutar(self):
pass
@abstractmethod
def deshacer(self):
pass
# Definir el receptor
class Luz:
def encender(self):
print("La luz está encendida.")
def apagar(self):
print("La luz está apagada.")
class Ventilador:
def encender(self):
print("El ventilador está encendido.")
def apagar(self):
print("El ventilador está apagado.")
# Definir los comandos concretos
class EncenderLuzCommand(Command):
def __init__(self, luz: Luz):
self.luz = luz
def ejecutar(self):
self.luz.encender()
def deshacer(self):
self.luz.apagar()
class ApagarLuzCommand(Command):
def __init__(self, luz: Luz):
self.luz = luz
def ejecutar(self):
self.luz.apagar()
def deshacer(self):
self.luz.encender()
class EncenderVentiladorCommand(Command):
def __init__(self, ventilador: Ventilador):
self.ventilador = ventilador
def ejecutar(self):
self.ventilador.encender()
def deshacer(self):
self.ventilador.apagar()
class ApagarVentiladorCommand(Command):
def __init__(self, ventilador: Ventilador):
self.ventilador = ventilador
def ejecutar(self):
self.ventilador.apagar()
def deshacer(self):
self.ventilador.encender()
# Definir el invocador
class ControlRemoto:
def __init__(self):
self.comandos = {}
self.historial_comandos = [] # Pila (stack) para almacenar comandos ejecutados
def asignar_comando(self, boton, comando: Command):
self.comandos[boton] = comando
def presionar_boton(self, boton):
if boton in self.comandos:
self.comandos[boton].ejecutar()
self.historial_comandos.append(self.comandos[boton]) # Agregar el comando al historial
else:
print(f"No hay comando asignado al botón {boton}")
def deshacer_comando(self):
if self.historial_comandos:
comando = self.historial_comandos.pop() # Sacar el último comando del historial
print("Deshaciendo la última acción...")
comando.deshacer()
else:
print("No hay acciones para deshacer.")
def deshacer_todos(self):
while self.historial_comandos:
self.deshacer_comando()
# Ejemplo de uso
if __name__ == "__main__":
# Crear receptores
luz = Luz()
ventilador = Ventilador()
# Crear comandos
encender_luz = EncenderLuzCommand(luz)
apagar_luz = ApagarLuzCommand(luz)
encender_ventilador = EncenderVentiladorCommand(ventilador)
apagar_ventilador = ApagarVentiladorCommand(ventilador)
# Configurar el control remoto
control_remoto = ControlRemoto()
control_remoto.asignar_comando("boton1", encender_luz)
control_remoto.asignar_comando("boton2", apagar_luz)
control_remoto.asignar_comando("boton3", encender_ventilador)
control_remoto.asignar_comando("boton4", apagar_ventilador)
# Usar el control remoto
control_remoto.presionar_boton("boton1") # La luz está encendida.
control_remoto.presionar_boton("boton1") # La luz está encendida nuevamente.
control_remoto.presionar_boton("boton4") # El ventilador está apagado.
# Deshacer la última acción
control_remoto.deshacer_comando() # Deshace: Apagar ventilador -> El ventilador está encendido.
# Deshacer todas las acciones
control_remoto.deshacer_todos() # Deshace las acciones: Encender luz dos veces.
Práctica
Para practicar, les invito a ver nuestro tutorial en video sobre el patrón de diseño Command en Python, presentado por Yuscu Mejía:
Recorda si te gusta los videos, te agradeceria si le pones un comentario o un like para saber si subir mas videos! 😮
Si no sabes que comentar, comenta con: estoy avanzando rapido con el curso de Programacion python 🙂 asi te detectaremos que viniste de aca, para una estadistica si conviene o no subir videos

Ejercicio
Ahora les propongo un ejercicio para aplicar lo que hemos aprendido sobre el patrón de diseño Command en Python, con la guía de Yuscu Mejía:
Ejercicio: Implementación del Patrón Command en un Servidor
En este ejercicio, vamos a crear un servidor que pueda aceptar y manejar diferentes tipos de peticiones usando el patrón Command. Implementaremos comandos para manejar peticiones GET y POST.
Sigue estos pasos para completar el ejercicio 1:
- Define una interfaz
Commandcon un métodoejecutar. - Implementa comandos concretos para manejar peticiones GET y POST.
- Define una clase
Servidorque pueda asignar comandos a rutas y ejecutarlos.
Sigue estos pasos para completar el ejercicio 2 :
- Implementa comandos concretos para manejar peticiones PUT y DELETE.
- Agrega funcionalidad para guardar y restablecer solicitudes.
Veamos cómo Yuscu Mejía implementaría la solución a este ejercicio:
#Ejercicio Resuelto 1
# Definir la interfaz Command
from abc import ABC, abstractmethod
class Command(ABC):
@abstractmethod
def ejecutar(self, request):
pass
# Definir el receptor
class Servidor:
def manejar_peticion(self, peticion):
print(f"Manejando petición: {peticion}")
# Definir los comandos concretos
class GetCommand(Command):
def __init__(self, servidor: Servidor):
self.servidor = servidor
def ejecutar(self, peticion):
self.servidor.manejar_peticion(f"GET {peticion}")
class PostCommand(Command):
def __init__(self, servidor: Servidor):
self.servidor = servidor
def ejecutar(self, peticion):
self.servidor.manejar_peticion(f"POST {peticion}")
# Definir el invocador
class ServidorWeb:
def __init__(self):
self.rutas = {}
def asignar_comando(self, ruta, comando: Command):
self.rutas[ruta] = comando
def manejar_ruta(self, ruta, peticion):
if ruta in self.rutas:
self.rutas[ruta].ejecutar(peticion)
else:
print(f"No hay comando asignado a la ruta {ruta}")
# Ejemplo de uso
if __name__ == "__main__":
# Crear el receptor
servidor = Servidor()
# Crear comandos
get_command = GetCommand(servidor)
post_command = PostCommand(servidor)
# Configurar el servidor web
servidor_web = ServidorWeb()
servidor_web.asignar_comando("/get", get_command)
servidor_web.asignar_comando("/post", post_command)
# Manejar rutas
servidor_web.manejar_ruta("/get", "datos") # Imprime: Manejando petición: GET datos
servidor_web.manejar_ruta("/post", "datos") # Imprime: Manejando petición: POST datos
servidor_web.manejar_ruta("/put", "datos") # Imprime: No hay comando asignado a la ruta /put
#Ejercicio Resuelto 2
# Definir la interfaz Command
from abc import ABC, abstractmethod
class Command(ABC):
@abstractmethod
def ejecutar(self, request):
pass
# Definir el receptor
class Servidor:
def __init__(self):
self.history = []
def manejar_peticion(self, peticion):
print(f"Manejando petición: {peticion}")
self.history.append(peticion)
def mostrar_historial(self):
print("Historial de peticiones:")
for idx, peticion in enumerate(self.history):
print(f"{idx+1}: {peticion}")
def reestablecer_historial(self):
self.history.clear()
print("Historial de peticiones reestablecido.")
# Definir los comandos concretos
class GetCommand(Command):
def __init__(self, servidor: Servidor):
self.servidor = servidor
def ejecutar(self, peticion):
self.servidor.manejar_peticion(f"GET {peticion}")
class PostCommand(Command):
def __init__(self, servidor: Servidor):
self.servidor = servidor
def ejecutar(self, peticion):
self.servidor.manejar_peticion(f"POST {peticion}")
class PutCommand(Command):
def __init__(self, servidor: Servidor):
self.servidor = servidor
def ejecutar(self, peticion):
self.servidor.manejar_peticion(f"PUT {peticion}")
class DeleteCommand(Command):
def __init__(self, servidor: Servidor):
self.servidor = servidor
def ejecutar(self, peticion):
self.servidor.manejar_peticion(f"DELETE {peticion}")
# Definir el invocador
class ServidorWeb:
def __init__(self):
self.rutas = {}
def asignar_comando(self, ruta, comando: Command):
self.rutas[ruta] = comando
def manejar_ruta(self, ruta, peticion):
if ruta in self.rutas:
self.rutas[ruta].ejecutar(peticion)
else:
print(f"No hay comando asignado a la ruta {ruta}")
# Ejemplo de uso
if __name__ == "__main__":
# Crear el receptor
servidor = Servidor()
# Crear comandos
get_command = GetCommand(servidor)
post_command = PostCommand(servidor)
put_command = PutCommand(servidor)
delete_command = DeleteCommand(servidor)
# Configurar el servidor web
servidor_web = ServidorWeb()
servidor_web.asignar_comando("/get", get_command)
servidor_web.asignar_comando("/post", post_command)
servidor_web.asignar_comando("/put", put_command)
servidor_web.asignar_comando("/delete", delete_command)
# Manejar rutas
servidor_web.manejar_ruta("/get", "datos") # Imprime: Manejando petición: GET datos
servidor_web.manejar_ruta("/post", "datos") # Imprime: Manejando petición: POST datos
servidor_web.manejar_ruta("/put", "datos") # Imprime: Manejando petición: PUT datos
servidor_web.manejar_ruta("/delete", "datos") # Imprime: Manejando petición: DELETE datos
servidor_web.manejar_ruta("/patch", "datos") # Imprime: No hay comando asignado a la ruta /patch
# Mostrar historial de peticiones
servidor.mostrar_historial() # Muestra el historial de todas las peticiones realizadas
# Reestablecer historial de peticiones
servidor.reestablecer_historial() # Reestablece el historial


hay varias lecciones de la seccion 8 que no hay videos o los videos subidos no corresponde a la lección