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 luceOutput:
Light is on
Light is off
Light is on
Light is offPunto 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.
Sfida
FacileIn 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 sollevareAttributeError) - 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)}")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
1Fondamenti di OOP
File esterniIntroduzione a OOPClassi vs. oggettiIl parametro selfMetodiAttributiMetodo costruttore (__init__)Riepilogo - Calcolatrice semplice4Ereditarietà
Ereditarietà di baseLa funzione super()Sovrascrittura dei metodiEreditarietà multiplaOrdine di risoluzione dei metodiRiepilogo - Gerarchia dei dipendenti7Metodi speciali
Introduzione ai metodi magiciSovraccarico degli operatoriMetodi magici dei contenitoriRiepilogo - Lista personalizzata10Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneModello SingletonModello FactoryModello ObserverModello Strategy2Decoratori
Introduzione ai decoratoriDecoratore di proprietàDecoratore di metodo staticoDecoratore di metodo di classe5Polimorfismo
Ripasso dell'override dei metodiDuck typingClassi e metodi astrattiProgettazione delle interfacceRiepilogo - Calcolatore di forme8Concetti avanzati di OOP
Composizione vs ereditarietàMixinMetodi statici e di classeDecorator di classeContext manager11Pattern di progettazione - Parte 2
Pattern dei comandiPattern adattatorePattern decoratorePattern metodo templatePattern statoPattern composito3Proprietà delle classi
Variabili di istanza e di classeDecorator delle proprietàAttributi privatiRiepilogo - Gestore di conti bancari6Incapsulamento
Membri pubblici, protetti e privatiModificatori di accessoOccultamento delle informazioniDecorator delle proprietà: avanzatiRipasso - Sistema di registri degli studenti12Progetto: Gestione della biblioteca
Panoramica del progettoClassi Libro e UtenteSistema di prestitoFunzionalità di ricercaInterfaccia amministratoreTest e integrazioneEsercitati da solo: Compilatore Python online