Patron de commande Python
Le patron Commande est un patron de conception comportemental qui encapsule une requête sous forme d’objet, ce qui vous permet de paramétrer des clients avec différentes requêtes. En termes simples,le patron Commande enveloppe des opérations (comme l’ouverture de fichiers, l’enregistrement de données) dans des objets indépendants,ce qui vous permet de traiter les opérations de la même manière que vous traitez les données.
Analogie dans la vie quotidienne
Imaginez le processus de commande dans un restaurant :
- Client(client) n'a pas besoin de savoir comment le cuisinier prépare le plat
- Serveur(invocateur) reçoit la commande mais ne cuisine pas personnellement
- Commande(objet commande) contient les informations spécifiques sur le plat
- Cuisinier(récepteur) réalise les opérations de cuisson effectives en fonction de la commande
Ce modèle bien divisé est une manifestation concrète du patron de conception Commande.
Pourquoi avoir besoin du patron de conception Commande ?
Limites de l'approche traditionnelle
En programmation traditionnelle, nous appelons généralement directement les méthodes d'un objet :
Exemple
def turn_on(self):
print("La lumière est allumée")
def turn_off(self):
print("La lumière est éteinte")
# Appel direct
light = Light()
light.turn_on()
light.turn_off()
Cette approche présente plusieurs problèmes :
- Couplage fort: L'appelant doit connaître l'interface spécifique du récepteur
- Difficile à étendre: L'ajout de nouvelles fonctionnalités nécessite de modifier le code existant
- Aucune prise en charge de l'annulation/rétablissement: Les opérations ne peuvent pas être annulées après exécution
Avantages du patron de conception Commande

Le patron de conception Commande résout les problèmes ci-dessus en introduisant une couche intermédiaire (objet commande), offrant :
- Découplage: Aucune dépendance directe entre l'appelant et le récepteur
- Extensibilité: Facile d'ajouter de nouvelles commandes
- Prise en charge de l'annulation/rétablissement: Peut enregistrer l'historique des opérations
- Prise en charge des files d'attente: les commandes peuvent être placées dans une file d'attente pour une exécution différée
Composants principaux du patron de conception Command
Structure de base
Le patron de conception Command comprend quatre rôles principaux :
| Rôle | Responsabilité | Exemple |
|---|---|---|
| Command(Interface de commande) | Déclare l'interface pour exécuter des opérations | execute(), undo() |
| ConcreteCommand(Commande concrète) | Implémente l'interface de commande et lie le récepteur | LightOnCommand, LightOffCommand |
| Receiver(Récepteur) | Sait comment effectuer des opérations | Light, TV |
| Invoker(Invocateur) | Stocke et exécute des commandes | RemoteControl |
Exemple de code complet
Implémentation de base
Comprenons le patron de conception Commande à travers un exemple de contrôle de l'éclairage d'une maison intelligente :
Exemple
from typing import List
# 1. Interface de commande
class Command(ABC):
@abstractmethod
def execute(self):
pass
@abstractmethod
def undo(self):
pass
# 2. Récepteur - Dispositif d'éclairage
class Light:
def turn_on(self):
print("💡 La lumière est allumée")
def turn_off(self):
print("💡 La lumière est éteinte")
# 3. Commande concrète - Commande d'allumage de la lumière
class LightOnCommand(Command):
def __init__(self, light: Light):
self.light = light
def execute(self):
self.light.turn_on()
def undo(self):
self.light.turn_off()
# 4. Commande concrète - Commande d'extinction de la lumière
class LightOffCommand(Command):
def __init__(self, light: Light):
self.light = light
def execute(self):
self.light.turn_off()
def undo(self):
self.light.turn_on()
# 5. Invocateur - Télécommande
class RemoteControl:
def __init__(self):
self.command = None
self.history: List[Command] = []
def set_command(self, command: Command):
self.command = command
def press_button(self):
if self.command:
self.command.execute()
self.history.append(self.command)
def press_undo(self):
if self.history:
last_command = self.history.pop()
last_command.undo()
# Code client
if __name__ == "__main__":
# Créer un appareil
living_room_light = Light()
# Créer les commandes
light_on = LightOnCommand(living_room_light)
light_off = LightOffCommand(living_room_light)
# Créer une télécommande
remote = RemoteControl()
# Test d'allumage de la lumière
print("=== Test d'allumage de la lumière ===")
remote.set_command(light_on)
remote.press_button()
# Test d'extinction de la lumière
print("\n=== Test d'extinction de la lumière ===")
remote.set_command(light_off)
remote.press_button()
# Test d'annulation
print("\n=== Test de l'opération d'annulation ===")
remote.press_undo() # Annuler l'extinction de la lumière, devrait allumer la lumière
remote.press_undo() # Annuler l'allumage de la lumière, devrait éteindre la lumière
Résultat de l'exécution:

Exemple d'application avancée
Télécommande universelle prenant en charge plusieurs appareils
Étendons l'exemple précédent et créons une télécommande universelle qui prend en charge plusieurs appareils :
Exemple
class TV:
def __init__(self, location: str):
self.location = location
self.is_on = False
self.volume = 50
def turn_on(self):
self.is_on = True
print(f"📺 {self.location} TV est allumée")
def turn_off(self):
self.is_on = False
print(f"📺 {self.location} TV est éteinte")
def set_volume(self, volume: int):
self.volume = volume
print(f"📺 {self.location} Volume de la TV réglé sur {volume}")
# Commandes liées à la TV
class TVOnCommand(Command):
def __init__(self, tv: TV):
self.tv = tv
self.previous_volume = 50
def execute(self):
self.previous_volume = self.tv.volume
self.tv.turn_on()
def undo(self):
self.tv.turn_off()
self.tv.volume = self.previous_volume
class TVVolumeUpCommand(Command):
def __init__(self, tv: TV):
self.tv = tv
self.previous_volume = 50
def execute(self):
self.previous_volume = self.tv.volume
self.tv.set_volume(min(100, self.tv.volume + 10))
def undo(self):
self.tv.set_volume(self.previous_volume)
# Commande macro - exécuter plusieurs commandes en une seule pression
class MacroCommand(Command):
def __init__(self, commands: List[Command]):
self.commands = commands
def execute(self):
for command in self.commands:
command.execute()
def undo(self):
# Annuler dans l'ordre inverse
for command in reversed(self.commands):
command.undo()
# Télécommande améliorée
class AdvancedRemoteControl:
def __init__(self, slot_count: int = 4):
self.on_commands: List[Command] = [None] * slot_count
self.off_commands: List[Command] = [None] * slot_count
self.history: List[Command] = []
def set_command(self, slot: int, on_command: Command, off_command: Command):
self.on_commands[slot] = on_command
self.off_commands[slot] = off_command
def press_on_button(self, slot: int):
if self.on_commands[slot]:
self.on_commands[slot].execute()
self.history.append(self.on_commands[slot])
def press_off_button(self, slot: int):
if self.off_commands[slot]:
self.off_commands[slot].execute()
self.history.append(self.off_commands[slot])
def press_undo(self):
if self.history:
last_command = self.history.pop()
last_command.undo()
# Test de la télécommande avancée
def test_advanced_remote():
print("=== Test de la télécommande universelle ===")
# Créer des appareils
living_room_light = Light()
bedroom_tv = TV("Chambre")
# Créer les commandes
light_on = LightOnCommand(living_room_light)
light_off = LightOffCommand(living_room_light)
tv_on = TVOnCommand(bedroom_tv)
tv_volume_up = TVVolumeUpCommand(bedroom_tv)
# Créer une commande macro - mode cinéma
cinema_mode = MacroCommand([light_off, tv_on, tv_volume_up])
# Configurer la télécommande
remote = AdvancedRemoteControl()
remote.set_command(0, light_on, light_off) # Emplacement 0 : contrôle de l'éclairage
remote.set_command(1, tv_on, TVOnCommand(bedroom_tv)) # Emplacement 1 : contrôle de la TV
remote.set_command(2, cinema_mode, light_on) # Emplacement 2 : mode cinéma
# Tester diverses fonctions
print("\n1. Allumer la lumière :")
remote.press_on_button(0)
print("\n2. Activer le mode cinéma :")
remote.press_on_button(2)
print("\n3. Annuler l'opération :")
remote.press_undo()
if __name__ == "__main__":
test_advanced_remote()
Paramètres et options du patron de commande
Extension de l'interface de commande
Dans les applications pratiques, l'interface de commande peut être étendue avec plus de fonctionnalités selon les besoins :
Exemple
@abstractmethod
def execute(self):
"""Exécuter la commande"""
pass
@abstractmethod
def undo(self):
"""Annuler la commande"""
pass
@abstractmethod
def redo(self):
"""Commande Refaire"""
pass
@abstractmethod
def can_execute(self) -> bool:
"""Vérifier si la commande peut être exécutée"""
pass
@abstractmethod
def get_description(self) -> str:
"""Obtenir la description de la commande"""
pass
File d'attente de commandes et exécution différée
Le patron de conception Commande prend naturellement en charge les files d'attente de commandes, ce qui est très utile dans de nombreux scénarios :
Exemple
def __init__(self):
self.queue: List[Command] = []
def add_command(self, command: Command):
self.queue.append(command)
def process_commands(self):
while self.queue:
command = self.queue.pop(0)
if command.can_execute():
command.execute()
def clear(self):
self.queue.clear()
Exercices pratiques
Exercice 1 : Implémenter un système de contrôle de climatisation
Essayez d'implémenter le patron de conception Commande pour un système de contrôle de climatisation :
Exemple
class AirConditioner:
def __init__(self):
self.temperature = 26
self.is_on = False
def turn_on(self):
# Implémenter la mise en marche du climatiseur
pass
def turn_off(self):
# Implémenter l'arrêt du climatiseur
pass
def set_temperature(self, temp: int):
# Implémenter le réglage de la température
pass
# Implémenter les classes de commande liées au climatiseur
# AirConditionerOnCommand
# AirConditionerOffCommand
# TemperatureUpCommand
# TemperatureDownCommand
Exercice 2 : implémenter la fonctionnalité d'annulation pour un éditeur de texte
Créez un éditeur de texte simple qui prend en charge la saisie de texte et les opérations d'annulation :
Exemple
def __init__(self):
self.content = ""
def add_text(self, text: str):
# Implémenter l'ajout de texte
pass
def delete_text(self, length: int):
# Implémenter la suppression de texte
pass
# Implémenter les commandes d’édition de texte
# AddTextCommand
# DeleteTextCommand
FAQ
Q : À quels scénarios le patron de conception Commande est-il adapté ?
A:Le patron de conception Commande est particulièrement adapté aux scénarios suivants :
- Lorsque les opérations doivent être paramétrées (par exemple, des boutons liés à différentes fonctions)
- Lorsque la fonctionnalité d’annulation/rétablissement est nécessaire
- Lorsque les opérations doivent être placées dans une file d’attente pour une exécution différée
- Lorsque les journaux d’opérations doivent être enregistrés
- Lorsque des opérations transactionnelles doivent être prises en charge
Q : Quels sont les inconvénients du patron Commande ?
A:Les principaux inconvénients incluent :
- Il peut créer un grand nombre de classes de commande
- Il augmente la complexité du code
- Il peut être trop lourd pour des opérations simples
Q : Comment choisir d'utiliser ou non le patron Commande ?
A:Tenez compte des points suivants :
- Si vous n'avez besoin que d'appels de méthode simples, n'utilisez pas le patron Commande
- Si une fonctionnalité d'annulation/rétablissement est nécessaire, il est fortement recommandé
- Si le système nécessite un fort découplage, le patron Commande est un bon choix
- Si les opérations doivent être mises en file d'attente ou journalisées, le patron Commande est très adapté
Résumé
Le patron Commande découple l'appelant du récepteur en encapsulant les opérations sous forme d'objets, offrant une puissante extensibilité. Bien qu'il ajoute une certaine complexité au code, le patron Commande est un patron de conception indispensable dans des scénarios tels que l'annulation/rétablissement, les files d'attente d'opérations et la journalisation.
Points clés :
- Le patron de conception Commande sépare « ce qu'il faut faire » de « qui l'exécute »
- Prend en charge des fonctionnalités avancées telles que l'annulation, le rétablissement et les files d'attente
- Facile à étendre avec de nouvelles commandes
- Largement utilisé dans les applications GUI, les systèmes transactionnels et le développement de jeux