Menu
Coddy logo textTech

Pattern dei comandi

Fa parte della sezione Programmazione orientata agli oggetti del percorso Python di Coddy. Lezione 49 di 64.

Il pattern Command incapsula una richiesta come oggetto, consentendoti di accodare operazioni, registrare richieste e supportare la funzionalità di annullamento. Separa l'oggetto che invoca l'operazione da quello che la esegue.

Ecco semplici classi di 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()

Ogni comando incapsula un'operazione specifica su un oggetto ricevitore.

Crea il ricevitore che esegue il lavoro vero e proprio:

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

Crea un invocatore che esegue i comandi:

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

Usa il pattern command:

# Crea il ricevitore
light = Light()

# Crea i comandi
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)

# Crea l'invocatore
remote = RemoteControl()

# Esegui comandi diversi
remote.set_command(light_on)
remote.press_button()

remote.set_command(light_off)
remote.press_button()

Aggiungi il supporto per le operazioni di annullamento:

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()  # Spegne la luce

Output:

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

Punto chiave: il pattern Command trasforma le richieste in oggetti che possono essere memorizzati, passati e eseguiti in seguito. L'invoker non deve sapere come eseguire l'operazione: deve solo chiamare execute() sull'oggetto comando. Questo consente funzionalità come annullare/ripristinare, accodare operazioni e registrare i comandi.

challenge icon

Sfida

Facile

In questa sfida, implementerai la classe base fondamentale Command in command.py: l'elemento essenziale del Command Pattern. Questo esercizio si concentra specificamente sull'incapsulamento: memorizzare i dati in modo privato ed esporli in sicurezza tramite una proprietà.

Modifica solo command.py seguendo i commenti TODO. I commenti TODO ti guideranno a:

  • Memorizzare il nome del comando come attributo privato (_name)
  • Esporlo tramite una proprietà di sola lettura (name)
  • Implementare un metodo display_info() che stampa il nome del comando

Nota: Questa sfida riguarda solo la classe base Command: l'Invoker, il Receiver e la funzionalità di annullamento saranno introdotti nelle lezioni successive. Completando questo passaggio, acquisirai le basi dell'incapsulamento su cui si fonda l'intero pattern.

La tua implementazione verrà verificata da driver.py, che controlla:

  • Funzionalità di base e formato dell'output
  • Casi limite (input vuoti, caratteri speciali, nomi lunghi)
  • Protezione della proprietà di sola lettura (il tentativo obj.name = ... dovrebbe sollevare AttributeError)
  • Presenza dell'attributo privato _name

Provalo tu

# Importa la classe Command da command.py
from command import Command

# Gestore completo dei casi di test
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:
        # Questa operazione dovrebbe fallire perché name è una proprietà di sola lettura
        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 iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore Python online