Menu
Coddy logo textTech

Padrão Command

Parte da seção Object Oriented Programming do Journey de Python da Coddy — lição 49 de 64.

O padrão Command encapsula uma solicitação como um objeto, permitindo que você enfileire operações, registre solicitações e suporte a funcionalidade de desfazer. Ele separa o objeto que chama a operação daquele que a executa.

Aqui estão algumas classes de comando simples:

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 uma operação específica em um objeto receptor.

Crie o receptor que realiza o trabalho real:

class Light:
    def turn_on(self):
        print("Light is on")
    
    def turn_off(self):
        print("Light is off")

Crie um invoker que executa comandos:

class RemoteControl:
    def __init__(self):
        self.command = None
    
    def set_command(self, command):
        self.command = command
    
    def press_button(self):
        self.command.execute()

Use o padrão command:

# Criar receptor
light = Light()

# Criar comandos
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)

# Criar invocador
remote = RemoteControl()

# Executar comandos diferentes
remote.set_command(light_on)
remote.press_button()

remote.set_command(light_off)
remote.press_button()

Adicione suporte para operações de desfazer:

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()  # Desliga a luz

Saída:

Light is on
Light is off
Light is on
Light is off

Ponto Chave: O Command Pattern transforma solicitações em objetos que podem ser armazenados, passados e executados posteriormente. O invoker não precisa saber como realizar a operação - ele apenas chama execute() no objeto de comando. Isso possibilita recursos como desfazer/refazer (undo/redo), enfileiramento de operações e log de comandos.

challenge icon

Desafio

Fácil

Neste desafio, você implementará a classe base fundamental Command em command.py — o bloco de construção essencial do Command Pattern. Este exercício foca especificamente em encapsulamento: armazenar dados de forma privada e expô-los com segurança através de uma propriedade.

Modifique apenas o arquivo command.py de acordo com os comentários TODO. Os comentários TODO guiarão você para:

  • Armazenar o nome do comando como um atributo privado (_name)
  • Expô-lo via uma propriedade somente leitura (name)
  • Implementar um método display_info() que imprime o nome do comando

Nota: Este desafio cobre apenas a classe base Command — o Invoker, Receiver e a funcionalidade de desfazer (undo) são introduzidos em lições posteriores. Concluir esta etapa fornece a base de encapsulamento sobre a qual o padrão completo é construído.

Sua implementação será testada pelo driver.py, que valida:

  • Funcionalidade básica e formato de saída
  • Casos de borda (entradas vazias, caracteres especiais, nomes longos)
  • Proteção de propriedade somente leitura (tentar obj.name = ... deve gerar um AttributeError)
  • Presença do atributo privado _name

Folha de consulta

O Command Pattern (Padrão de Comando) encapsula uma solicitação como um objeto, permitindo que você enfileire operações, registre solicitações e suporte a funcionalidade de desfazer.

Estrutura básica de comando:

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()

Crie um receptor (receiver) que realize o trabalho real:

class Light:
    def turn_on(self):
        print("Light is on")
    
    def turn_off(self):
        print("Light is off")

Crie um invocador (invoker) que execute comandos:

class RemoteControl:
    def __init__(self):
        self.command = None
    
    def set_command(self, command):
        self.command = command
    
    def press_button(self):
        self.command.execute()

Exemplo de uso:

# Criar receptor
light = Light()

# Criar comandos
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)

# Criar invocador
remote = RemoteControl()

# Executar comandos diferentes
remote.set_command(light_on)
remote.press_button()

remote.set_command(light_off)
remote.press_button()

Adicionar funcionalidade de desfazer:

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()

Experimente você mesmo

# Importa a classe Command do arquivo command.py
from command import Command

# Manipulador abrangente de casos de teste
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:
        # Isso deve falhar, pois name é uma propriedade somente leitura
        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)}")
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Object Oriented Programming