Menu
Coddy logo textTech

Patron Commande

Fait partie de la section Programmation orientée objet du Journey Python de Coddy. Leçon 49 sur 64.

Le patron Command encapsule une requête sous forme d’objet, ce qui vous permet de mettre des opérations en file d’attente, de journaliser les 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 modèle de 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 la prise en charge des 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 modèle Commande transforme les requêtes en objets qui peuvent être stockés, transmis et exécutés ultérieurement. L’invocateur n’a pas besoin de savoir comment effectuer l’opération : il appelle simplement execute() sur l’objet commande. Cela permet des fonctionnalités comme l’annulation/rétablissement, 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 Command fondamentale dans command.py : le composant essentiel du modèle Command. Cet exercice porte 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 conformément aux 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 couvre uniquement la classe de base Command : l’Invoker, le Receiver et la fonctionnalité d’annulation sont présentés dans des leçons ultérieures. Terminer cette étape vous fournit les bases d’encapsulation sur lesquelles repose le modèle complet.

Votre implémentation sera testée par driver.py, qui vérifie :

  • Les fonctionnalités 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 AttributeError)
  • La présence de l’attribut privé _name

Essayez vous-même

# Importer la classe Command depuis command.py
from command import Command

# Gestionnaire complet de cas de 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:
        # Ceci 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 Programmation orientée objet

Entraînez-vous par vous-même : Compilateur Python en ligne