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 llama a 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:
# Crear receptor
light = Light()
# Crear comandos
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)
# Crear invocador
remote = RemoteControl()
# Ejecutar diferentes comandos
remote.set_command(light_on)
remote.press_button()
remote.set_command(light_off)
remote.press_button()Añade compatibilidad con 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, transferir 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 Command fundamental 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 de acuerdo con 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 muestre el nombre del comando
Nota: Este desafío abarca únicamente la clase base Command: el Invoker, el Receiver y la funcionalidad de deshacer se introducirán 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 será probada por driver.py, que valida:
- La funcionalidad básica y el formato de salida
- 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
# Manejador integral 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 ya que 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
Archivos externosIntroducción a la POOClases vs ObjetosEl parámetro selfMétodosAtributosMétodo constructor (__init__)Resumen - Calculadora simple4Herencia
Herencia básicaLa función super()Sobrescritura de métodosHerencia múltipleOrden de resolución de métodosResumen - 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 propertyDecorador de métodos estáticosDecorador de métodos de clase5Polimorfismo
Sobreescritura de métodos revisitadaDuck TypingClases y métodos abstractosDiseño de interfacesResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. 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 de claseDecoradores de propiedadesAtributos privadosResumen - Gestor de cuentas bancariasPractica por tu cuenta: Compilador de Python online