Concept Fondamental
Le patron de conception Commande (Command Pattern) est un patron comportemental qui transforme une requête ou une action en un objet autonome. Cet objet encapsule toutes les informations nécessaires à son exécution, y compris la méthode à appeler, l'objet qui la reçoit et les paramètres associés.
Cette approche permet de séparer clairement l'objet qui déclenche l'opération (l'invocateur) de l'objet qui possède la logique pour la traiter (le récepteur). L'invocateur n'a aucune connaissance du récepteur réel, il se contente d'invoquer la commande.
Avantages
- Découplage fort : L'émetteur de la requête est complètement isolé du récepteur, ce qui améliore la modularité du code.
- Extensibilité : L'ajout de nouveles opérations se fait par la création de nouvelles classes concrètes, respectant ainsi le principe ouvert/fermé (Open-Closed Principle).
- Gestion de l'historique : Il est très facile d'implémenter des fonctionnalités d'annulation (undo) et de rétablissement (redo) en stockant les commandes exécutées dans une pile ou une file.
Inconvénients
Prolifération des classes : Chaque action spécifique nécessite sa propre classe concrète. Si le système comporte de nombreuses opérations simples, cela peut entraîner une augmentation significative du nombre de classes dans l'architecture.
Étude de Cas : Système de Contrôle Domotique
Pour illustrer ce patron, concevons un tableau de bord domotique capable de gérer l'éclairage d'une pièce. Le tableau de bord servira d'invocateur, tandis que le système d'éclairage sera le récepteur.
Définition des Interfaces et Classes
1. Interface de Commande
public interface OperationDomotique {
void executer();
void inverser();
}
2. Classes de Commandes Concrètes
public class ActiverEclairage implements OperationDomotique {
private final SystemeEclairage eclairage;
public ActiverEclairage(SystemeEclairage eclairage) {
this.eclairage = eclairage;
}
@Override
public void executer() {
eclairage.activer();
}
@Override
public void inverser() {
eclairage.desactiver();
}
}
public class DesactiverEclairage implements OperationDomotique {
private final SystemeEclairage eclairage;
public DesactiverEclairage(SystemeEclairage eclairage) {
this.eclairage = eclairage;
}
@Override
public void executer() {
eclairage.desactiver();
}
@Override
public void inverser() {
eclairage.activer();
}
}
3. Classe Récepteur
public class SystemeEclairage {
public void activer() {
System.out.println("L'éclairage de la pièce est maintenant allumé.");
}
public void desactiver() {
System.out.println("L'éclairage de la pièce est maintenant éteint.");
}
}
4. Classe Invocateur
import java.util.Stack;
public class TableauDeBord {
private final Stack<OperationDomotique> historique = new Stack<>();
private OperationDomotique operationCourante;
public void definirOperation(OperationDomotique operation) {
this.operationCourante = operation;
}
public void lancer() {
if (operationCourante != null) {
operationCourante.executer();
historique.push(operationCourante);
}
}
public void annulerDerniereAction() {
if (!historique.isEmpty()) {
OperationDomotique derniereOp = historique.pop();
derniereOp.inverser();
}
}
}
Exécution et Démonstration
public class Main {
public static void main(String[] args) {
SystemeEclairage eclairage = new SystemeEclairage();
OperationDomotique allumer = new ActiverEclairage(eclairage);
OperationDomotique eteindre = new DesactiverEclairage(eclairage);
TableauDeBord tableau = new TableauDeBord();
// Allumer la lumière
tableau.definirOperation(allumer);
tableau.lancer();
// Annuler l'allumage (éteint la lumière)
tableau.annulerDerniereAction();
// Éteindre explicitement
tableau.definirOperation(eteindre);
tableau.lancer();
// Annuler l'extinction (rallume la lumière)
tableau.annulerDerniereAction();
}
}
Analyse de l'Architecture
- OperationDomotique : Définit le contrat avec les méthodes
executerpour déclencher l'action etinverserpour revenir à l'état précédent. - Commandes Concrètes :
ActiverEclairageetDesactiverEclairageencapsulent les requêtes et maintiennent une référence vers le récepteur via l'injection de dépendances. - SystemeEclairage : Contient la logique métier réelle pour manipuler l'état des lumières.
- TableauDeBord : Gère le déclenchement des commandes. Contrairement à une implémentation basique, il utilise une pile (
Stack) pour enregistrer l'historqiue des opérations, permettant ainsi une gestion robuste et séquentielle des annulations.