Menu
Coddy logo textTech

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ère

Sortie :

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

Point 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.

challenge icon

Défi

Facile

Dans 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 une AttributeError)
  • 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)}")
quiz iconTestez-vous

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