Le modèle de conception Command est l’un des modèles de conception comportementaux. Le modèle de conception de commande est utilisé pour implémenter un couplage lâche dans un modèle de requête-réponse.
Modèle de conception Command
Dans le modèle de conception de commande, la requête est envoyée à l’
invocateur
qui la transmet à l’objet commande
encapsulé. L’objet de commande transmet la requête à la méthode appropriée du récepteur
pour effectuer l’action spécifique. Le programme client crée l’objet récepteur, puis l’attache à la commande. Ensuite, il crée l’objet invocateur et attache l’objet de commande pour effectuer une action. Maintenant, lorsque le programme client exécute l’action, celle-ci est traitée en fonction de la commande et de l’objet récepteur.
Exemple de modèle de conception de commande
Nous examinerons un scénario réel où nous pouvons mettre en œuvre le motif de commande. Disons que nous voulons fournir une utilité de système de fichiers avec des méthodes pour ouvrir, écrire et fermer le fichier. Cette utilité de système de fichiers devrait prendre en charge plusieurs systèmes d’exploitation tels que Windows et Unix. Pour mettre en œuvre notre utilité de système de fichiers, tout d’abord, nous devons créer les classes de réception qui feront effectivement tout le travail. Comme nous codons en termes de interface en java, nous pouvons avoir l’interface FileSystemReceiver
et ses classes d’implémentation pour différentes saveurs de système d’exploitation telles que Windows, Unix, Solaris, etc.
Classes de réception du motif de commande
package com.journaldev.design.command;
public interface FileSystemReceiver {
void openFile();
void writeFile();
void closeFile();
}
L’interface FileSystemReceiver définit le contrat pour les classes d’implémentation. Pour simplifier, je crée deux saveurs de classes de réception pour travailler avec les systèmes Unix et Windows.
package com.journaldev.design.command;
public class UnixFileSystemReceiver implements FileSystemReceiver {
@Override
public void openFile() {
System.out.println("Opening file in unix OS");
}
@Override
public void writeFile() {
System.out.println("Writing file in unix OS");
}
@Override
public void closeFile() {
System.out.println("Closing file in unix OS");
}
}
package com.journaldev.design.command;
public class WindowsFileSystemReceiver implements FileSystemReceiver {
@Override
public void openFile() {
System.out.println("Opening file in Windows OS");
}
@Override
public void writeFile() {
System.out.println("Writing file in Windows OS");
}
@Override
public void closeFile() {
System.out.println("Closing file in Windows OS");
}
}
Avez-vous remarqué l’annotation Override et si vous vous demandez pourquoi elle est utilisée, veuillez lire les annotations java et les avantages de l’annotation override. Maintenant que nos classes de réception sont prêtes, nous pouvons passer à la mise en œuvre de nos classes de commande.
Interface et Implémentations du Modèle de Commande
Nous pouvons utiliser une interface ou une classe abstraite pour créer notre Commande de base, c’est une décision de conception qui dépend de vos besoins. Nous optons pour une interface car nous n’avons aucune implémentation par défaut.
package com.journaldev.design.command;
public interface Command {
void execute();
}
À présent, nous devons créer des implémentations pour tous les types d’actions effectuées par le destinataire. Comme nous avons trois actions, nous créerons trois implémentations de Commande. Chaque implémentation de Commande transmettra la requête à la méthode appropriée du destinataire.
package com.journaldev.design.command;
public class OpenFileCommand implements Command {
private FileSystemReceiver fileSystem;
public OpenFileCommand(FileSystemReceiver fs){
this.fileSystem=fs;
}
@Override
public void execute() {
// La commande "open" transmet la requête à la méthode "openFile"
this.fileSystem.openFile();
}
}
package com.journaldev.design.command;
public class CloseFileCommand implements Command {
private FileSystemReceiver fileSystem;
public CloseFileCommand(FileSystemReceiver fs){
this.fileSystem=fs;
}
@Override
public void execute() {
this.fileSystem.closeFile();
}
}
package com.journaldev.design.command;
public class WriteFileCommand implements Command {
private FileSystemReceiver fileSystem;
public WriteFileCommand(FileSystemReceiver fs){
this.fileSystem=fs;
}
@Override
public void execute() {
this.fileSystem.writeFile();
}
}
Maintenant que nous avons le destinataire et les implémentations des commandes prêts, nous pouvons passer à la mise en œuvre de la classe invoqueur.
Classe Invoqueur du Modèle de Commande
L’invoqueur est une classe simple qui encapsule la Commande et transmet la requête à l’objet de commande pour la traiter.
package com.journaldev.design.command;
public class FileInvoker {
public Command command;
public FileInvoker(Command c){
this.command=c;
}
public void execute(){
this.command.execute();
}
}
Notre implémentation de l’utilitaire de système de fichiers est prête et nous pouvons passer à l’écriture d’un programme client de modèle de commande simple. Mais avant cela, je fournirai une méthode utilitaire pour créer l’objet approprié FileSystemReceiver
. Puisque nous pouvons utiliser la classe System pour obtenir des informations sur le système d’exploitation, nous utiliserons ceci ou sinon nous pouvons utiliser le modèle de conception Factory pour retourner le type approprié en fonction de l’entrée.
package com.journaldev.design.command;
public class FileSystemReceiverUtil {
public static FileSystemReceiver getUnderlyingFileSystem(){
String osName = System.getProperty("os.name");
System.out.println("Underlying OS is:"+osName);
if(osName.contains("Windows")){
return new WindowsFileSystemReceiver();
}else{
return new UnixFileSystemReceiver();
}
}
}
Passons maintenant à la création de notre programme client d’exemple de modèle de commande qui consommera notre utilitaire de système de fichiers.
package com.journaldev.design.command;
public class FileSystemClient {
public static void main(String[] args) {
//Création de l'objet receveur
FileSystemReceiver fs = FileSystemReceiverUtil.getUnderlyingFileSystem();
//création de la commande et association avec le receveur
OpenFileCommand openFileCommand = new OpenFileCommand(fs);
//Création de l'invocateur et association avec la commande
FileInvoker file = new FileInvoker(openFileCommand);
//exécution de l'action sur l'objet invocateur
file.execute();
WriteFileCommand writeFileCommand = new WriteFileCommand(fs);
file = new FileInvoker(writeFileCommand);
file.execute();
CloseFileCommand closeFileCommand = new CloseFileCommand(fs);
file = new FileInvoker(closeFileCommand);
file.execute();
}
}
Remarquez que le client est responsable de créer le type approprié d’objet de commande. Par exemple, si vous voulez écrire un fichier, vous ne devez pas créer l’objet CloseFileCommand
. Le programme client est également responsable d’attacher le receveur à la commande, puis la commande à la classe invocateur. La sortie du programme d’exemple de modèle de commande ci-dessus est :
Underlying OS is:Mac OS X
Opening file in unix OS
Writing file in unix OS
Closing file in unix OS
Diagramme de classe du Modèle de Commande
Voici le diagramme de classe pour notre implémentation d’utilitaire de système de fichiers.
Points Importants du Modèle de Conception de Commande
- La commande est au cœur du modèle de conception de commande, elle définit le contrat pour l’implémentation.
- L’implémentation du récepteur est séparée de l’implémentation de la commande.
- Les classes d’implémentation de la commande choisissent la méthode à invoquer sur l’objet récepteur, pour chaque méthode du récepteur, il y aura une implémentation de commande. Elle agit comme un pont entre le récepteur et les méthodes d’action.
- La classe invoqueur se contente de transmettre la demande du client à l’objet commande.
- Le client est responsable d’instancier la commande et l’implémentation du récepteur appropriées, puis de les associer ensemble.
- Le client est également responsable d’instancier l’objet invoqueur et d’associer l’objet commande avec lui et d’exécuter la méthode d’action.
- Le modèle de conception de commande est facilement extensible, nous pouvons ajouter de nouvelles méthodes d’action dans les récepteurs et créer de nouvelles implémentations de commande sans modifier le code client.
- L’inconvénient du modèle de conception de commande est que le code devient volumineux et confus avec un grand nombre de méthodes d’action et en raison de tant d’associations.
Modèle de conception Command JDK Exemple
Interface Runnable (java.lang.Runnable) et Swing Action (javax.swing.Action) utilisent le modèle de conception command.
Source:
https://www.digitalocean.com/community/tutorials/command-design-pattern