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èreSortie :
Light is on
Light is off
Light is on
Light is offPoint 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.
Défi
FacileDans 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 leverAttributeError) - 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)}")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
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 bancaireEntraînez-vous par vous-même : Compilateur Python en ligne