Patron Commande
Fait partie de la section Object Oriented Programming du Journey Python de Coddy — leçon 49 sur 64.
Le pattern Command encapsule une requête sous forme d'objet, vous permettant de mettre des opérations en file d'attente, de journaliser des requêtes et de prendre en charge la fonctionnalité d'annulation. Il sépare l'objet qui appelle l'opération de celui qui l'exécute.
Voici des classes de commande 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()Chaque commande encapsule une opération spécifique sur un objet récepteur.
Créez le récepteur qui effectue le travail réel :
class Light:
def turn_on(self):
print("Light is on")
def turn_off(self):
print("Light is off")Créez un invocateur qui exécute des commandes :
class RemoteControl:
def __init__(self):
self.command = None
def set_command(self, command):
self.command = command
def press_button(self):
self.command.execute()Utilisez le patron de conception Commande :
# Créer le récepteur
light = Light()
# Créer les commandes
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)
# Créer l'invocateur
remote = RemoteControl()
# Exécuter différentes commandes
remote.set_command(light_on)
remote.press_button()
remote.set_command(light_off)
remote.press_button()Ajoutez le support pour les opérations d'annulation :
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() # Éteint la lumièreSortie :
Light is on
Light is off
Light is on
Light is offPoint clé : Le patron de conception Commande (Command Pattern) transforme les requêtes en objets qui peuvent être stockés, transmis et exécutés ultérieurement. L'appelant (invoker) n'a pas besoin de savoir comment effectuer l'opération - il appelle simplement execute() sur l'objet de commande. Cela permet des fonctionnalités telles que l'annulation/rétablissement (undo/redo), la mise en file d'attente des opérations et la journalisation des commandes.
Défi
FacileDans ce défi, vous allez implémenter la classe de base fondamentale Command dans command.py — la brique essentielle du Design Pattern Command. Cet exercice se concentre spécifiquement sur l'encapsulation : stocker les données de manière privée et les exposer en toute sécurité via une propriété.
Modifiez uniquement command.py en suivant les commentaires TODO. Les commentaires TODO vous guideront pour :
- Stocker le nom de la commande en tant qu'attribut privé (
_name) - L'exposer via une propriété en lecture seule (
name) - Implémenter une méthode
display_info()qui affiche le nom de la commande
Remarque : Ce défi ne couvre que la classe de base Command — l'Invoker, le Receiver et la fonctionnalité d'annulation (undo) sont introduits dans les leçons suivantes. Réussir cette étape vous donne la base d'encapsulation sur laquelle repose le pattern complet.
Votre implémentation sera testée par driver.py, qui valide :
- Le fonctionnement de base et le format de sortie
- Les cas limites (entrées vides, caractères spéciaux, noms longs)
- La protection de la propriété en lecture seule (tenter
obj.name = ...doit lever uneAttributeError) - La présence de l'attribut privé
_name
Aide-mémoire
Le patron de conception Commande (Command Pattern) encapsule une requête sous forme d'objet, vous permettant de mettre en file d'attente des opérations, de journaliser des requêtes et de prendre en charge la fonctionnalité d'annulation.
Structure de commande de base :
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()Créez un récepteur qui effectue le travail réel :
class Light:
def turn_on(self):
print("Light is on")
def turn_off(self):
print("Light is off")Créez un invocateur qui exécute les commandes :
class RemoteControl:
def __init__(self):
self.command = None
def set_command(self, command):
self.command = command
def press_button(self):
self.command.execute()Exemple d'utilisation :
# Créer le récepteur
light = Light()
# Créer les commandes
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)
# Créer l'invocateur
remote = RemoteControl()
# Exécuter différentes commandes
remote.set_command(light_on)
remote.press_button()
remote.set_command(light_off)
remote.press_button()Ajouter la fonctionnalité d'annulation :
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()Essayez vous-même
# Importer la classe Command depuis command.py
from command import Command
# Gestionnaire de cas de test complet
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:
# Cela devrait échouer car name est une propriété en lecture seule
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)}")Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Object Oriented Programming
1Les fondamentaux de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe paramètre selfMéthodesAttributsMéthode constructeur (__init__)Récapitulatif - Calculatrice simple4Héritage
Héritage de baseLa fonction super()Redéfinition de méthodeHéritage multipleOrdre de résolution des méthodesRécapitulatif - Hiérarchie des employés7Méthodes spéciales
Introduction aux méthodes magiquesSurcharge d'opérateursMéthodes magiques de conteneurRécapitulatif - Liste personnalisée10Patrons de conception, Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FactoryPatron ObserverPatron Strategy2Décorateurs
Introduction aux décorateursDécorateur de propriétéDécorateur de méthode statiqueDécorateur de méthode de classe5Polymorphisme
Retour sur la redéfinition de méthodeDuck TypingClasses et méthodes abstraitesConception d'interfacesRécapitulatif - Calculateur de formes8Concepts avancés de la POO
Composition vs HéritageMixinsMéthodes statiques et de classeDécorateurs de classeGestionnaires de contexte11Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode TemplatePatron ÉtatPatron Composite3Propriétés de classe
Variables d'instance vs variables de classeDécorateurs de propriétéAttributs privésRécapitulatif - Gestionnaire de compte bancaire