Patrón Command
Parte de la sección Programación orientada a objetos del Journey de Python de Coddy. Lección 49 de 64.
El patrón Command encapsula una solicitud como un objeto, lo que te permite poner operaciones en cola, registrar solicitudes y admitir la funcionalidad de deshacer. Separa el objeto que invoca la operación del que la ejecuta.
Aquí tienes clases de comandos sencillas:
class Command:
def execute(self):
pass
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()Cada comando encapsula una operación específica en un objeto receptor.
Crea el receptor que realiza el trabajo real:
class Light:
def turn_on(self):
print("Light is on")
def turn_off(self):
print("Light is off")Crea un invocador que ejecute comandos:
class RemoteControl:
def __init__(self):
self.command = None
def set_command(self, command):
self.command = command
def press_button(self):
self.command.execute()Usa el patrón de comandos:
# Crea el receptor
light = Light()
# Crea los comandos
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)
# Crea el invocador
remote = RemoteControl()
# Ejecuta diferentes comandos
remote.set_command(light_on)
remote.press_button()
remote.set_command(light_off)
remote.press_button()Añade compatibilidad con las operaciones de deshacer:
class UndoableCommand(Command):
def undo(self):
pass
class LightOnCommand(UndoableCommand):
def __init__(self, light):
self.light = light
def execute(self):
self.light.turn_on()
def undo(self):
self.light.turn_off()
class SmartRemote:
def __init__(self):
self.last_command = None
def execute_command(self, command):
command.execute()
self.last_command = command
def undo(self):
if self.last_command:
self.last_command.undo()
smart_remote = SmartRemote()
smart_remote.execute_command(LightOnCommand(light))
smart_remote.undo() # Apaga la luzSalida:
Light is on
Light is off
Light is on
Light is offPunto clave: El patrón Command convierte las solicitudes en objetos que se pueden almacenar, pasar de un lado a otro y ejecutar más tarde. El invocador no necesita saber cómo realizar la operación; simplemente llama a execute() en el objeto de comando. Esto permite funciones como deshacer/rehacer, poner operaciones en cola y registrar comandos.
Desafío
FácilEn este desafío, implementarás la clase base fundamental Command en command.py: el componente esencial del patrón Command. Este ejercicio se centra específicamente en la encapsulación: almacenar datos de forma privada y exponerlos de manera segura mediante una propiedad.
Modifica únicamente command.py según los comentarios TODO. Los comentarios TODO te guiarán para:
- Almacenar el nombre del comando como un atributo privado (
_name) - Exponerlo mediante una propiedad de solo lectura (
name) - Implementar un método
display_info()que imprima el nombre del comando
Nota: Este desafío abarca únicamente la clase base Command: el Invoker, el Receiver y la funcionalidad de deshacer se presentan en lecciones posteriores. Completar este paso te proporciona los fundamentos de encapsulación sobre los que se construye el patrón completo.
Tu implementación se probará mediante driver.py, que valida:
- La funcionalidad básica y el formato de salida
- Los casos límite (entradas vacías, caracteres especiales, nombres largos)
- La protección de la propiedad de solo lectura (intentar
obj.name = ...debería generarAttributeError) - La presencia del atributo privado
_name
Pruébalo tú mismo
# Importa la clase Command desde command.py
from command import Command
# Gestor completo de casos de prueba
test_case = input()
if test_case == "basic_test":
obj = Command("Test Name")
obj.display_info()
elif test_case == "validation_test":
obj = Command("Validation Test")
print(f"Name: {obj.name}")
elif test_case == "empty_name_test":
obj = Command("")
obj.display_info()
print(f"Empty name handled: {'Yes' if obj.name == '' else 'No'}")
elif test_case == "property_access_test":
obj = Command("Property Test")
original_name = obj.name
try:
# Esto debería fallar porque name es una propiedad de solo lectura
obj.name = "Modified Name"
print("Property protection failed")
except AttributeError:
print("Property protected successfully")
print(f"Name unchanged: {obj.name == original_name}")
elif test_case == "multiple_commands_test":
commands = [
Command("First Command"),
Command("Second Command"),
Command("Third Command")
]
for cmd in commands:
cmd.display_info()
elif test_case == "attribute_test":
obj = Command("Attribute Test")
has_private_name = hasattr(obj, "_name")
print(f"Has _name attribute: {has_private_name}")
elif test_case == "special_chars_test":
obj = Command("!@#$%^&*()_+{}[]|\\:;\"'<>,.?/")
obj.display_info()
elif test_case == "long_name_test":
long_name = "A" * 100
obj = Command(long_name)
obj.display_info()
print(f"Name length: {len(obj.name)}")Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación orientada a objetos
1Fundamentos de la POO
Introducción a la POOArchivos externosClases vs. objetosEl parámetro selfMétodosAtributosMétodo constructor (__init__)Repaso: calculadora sencilla4Herencia
Herencia básicaLa función super()Sobrescritura de métodosHerencia múltipleOrden de resolución de métodosRepaso: jerarquía de empleados7Métodos especiales
Introducción a los métodos mágicosSobrecarga de operadoresMétodos mágicos de contenedoresRepaso: lista personalizada10Patrones de diseño, parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy2Decoradores
Introducción a los decoradoresDecorador de propiedadDecorador de método estáticoDecorador de método de clase5Polimorfismo
Repaso de la sobrescritura de métodosTipado patoClases y métodos abstractosDiseño de interfacesRepaso: calculadora de formas8Conceptos avanzados de POO
Composición frente a herenciaMixinsMétodos estáticos y de claseDecoradores de claseGestores de contexto11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Propiedades de clase
Variables de instancia vs. variables de claseDecoradores de propiedadesAtributos privadosRepaso: gestor de cuentas bancariasPractica por tu cuenta: Compilador de Python online