Il pattern Command è uno dei pattern di progettazione comportamentali. Il pattern di progettazione Command viene utilizzato per implementare il disaccoppiamento in un modello di richiesta-risposta.
Pattern Command
Nel pattern Command, la richiesta viene inviata all’
invoker
e l’invoker la passa all’oggetto command
incapsulato. L’oggetto Command passa la richiesta al metodo appropriato del Receiver
per eseguire l’azione specifica. Il programma client crea l’oggetto ricevitore e lo collega al comando. Quindi crea l’oggetto invoker e collega l’oggetto comando per eseguire un’azione. Ora, quando il programma client esegue l’azione, viene elaborata in base al comando e all’oggetto ricevitore.
Esempio di Pattern Command
Noi esamineremo uno scenario della vita reale in cui possiamo implementare il pattern di comando. Supponiamo di voler fornire un’utilità di sistema di file con metodi per aprire, scrivere e chiudere un file. Questa utilità di sistema di file dovrebbe supportare più sistemi operativi come Windows e Unix. Per implementare la nostra utilità di sistema di file, prima di tutto, dobbiamo creare le classi riceventi che svolgeranno effettivamente tutto il lavoro. Poiché scriviamo in termini di interfaccia in Java, possiamo avere un’interfaccia FileSystemReceiver
e le relative classi di implementazione per diverse varianti di sistemi operativi come Windows, Unix, Solaris, ecc.
Classi Riceventi del Pattern di Comando
package com.journaldev.design.command;
public interface FileSystemReceiver {
void openFile();
void writeFile();
void closeFile();
}
L’interfaccia FileSystemReceiver definisce il contratto per le classi di implementazione. Per semplicità, sto creando due varianti di classi riceventi per lavorare con i sistemi Unix e 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");
}
}
Hai notato l’annotazione Override e se ti chiedi perché viene utilizzata, leggi per favore annotazioni Java e benefici dell’annotazione Override. Ora che le nostre classi riceventi sono pronte, possiamo passare all’implementazione delle nostre classi di comando.
Interfaccia e Implementazioni del Pattern di Comando
Possiamo utilizzare un’interfaccia o una classe astratta per creare il nostro comando di base; è una decisione di progettazione e dipende dalle tue esigenze. Optiamo per l’interfaccia poiché non abbiamo implementazioni predefinite.
package com.journaldev.design.command;
public interface Command {
void execute();
}
Ora dobbiamo creare le implementazioni per tutti i diversi tipi di azioni eseguite dal ricevitore. Poiché abbiamo tre azioni, creeremo tre implementazioni del comando. Ogni implementazione del comando inoltrerà la richiesta al metodo appropriato del ricevitore.
package com.journaldev.design.command;
public class OpenFileCommand implements Command {
private FileSystemReceiver fileSystem;
public OpenFileCommand(FileSystemReceiver fs){
this.fileSystem=fs;
}
@Override
public void execute() {
//il comando "apri" sta inoltrando la richiesta al metodo 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();
}
}
Ora abbiamo il ricevitore e le implementazioni del comando pronte, possiamo passare a implementare la classe invocante.
Classe Invocante del Pattern di Comando
L’invocatore è una classe semplice che incapsula il comando e passa la richiesta all’oggetto comando per elaborarla.
package com.journaldev.design.command;
public class FileInvoker {
public Command command;
public FileInvoker(Command c){
this.command=c;
}
public void execute(){
this.command.execute();
}
}
La nostra implementazione dell’utilità del sistema di file è pronta e possiamo passare a scrivere un semplice programma client basato sul pattern di comando. Ma prima di ciò fornirò un metodo di utilità per creare l’oggetto FileSystemReceiver
appropriato. Dal momento che possiamo utilizzare la classe System per ottenere le informazioni sul sistema operativo, useremo questo oppure possiamo utilizzare il pattern Factory per restituire il tipo appropriato in base all’input.
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();
}
}
}
Passiamo ora alla creazione del nostro esempio di programma client basato sul pattern di comando che consumerà la nostra utilità del sistema di file.
package com.journaldev.design.command;
public class FileSystemClient {
public static void main(String[] args) {
//Creazione dell'oggetto ricevitore
FileSystemReceiver fs = FileSystemReceiverUtil.getUnderlyingFileSystem();
//creazione del comando e associazione con il ricevitore
OpenFileCommand openFileCommand = new OpenFileCommand(fs);
//Creazione del chiamante e associazione con il comando
FileInvoker file = new FileInvoker(openFileCommand);
//eseguire l'azione sull'oggetto chiamante
file.execute();
WriteFileCommand writeFileCommand = new WriteFileCommand(fs);
file = new FileInvoker(writeFileCommand);
file.execute();
CloseFileCommand closeFileCommand = new CloseFileCommand(fs);
file = new FileInvoker(closeFileCommand);
file.execute();
}
}
Si noti che il client è responsabile della creazione del tipo appropriato di oggetto comando. Ad esempio, se si desidera scrivere un file, non si deve creare un oggetto CloseFileCommand
. Il programma client è anche responsabile di collegare il ricevitore al comando e quindi il comando alla classe chiamante. L’output del programma di esempio sopra è:
Underlying OS is:Mac OS X
Opening file in unix OS
Writing file in unix OS
Closing file in unix OS
Diagramma di classe del pattern di comando
Ecco il diagramma delle classi per la nostra implementazione dell’utilità del file system.
Punti importanti del Pattern Command
- Il comando è il nucleo del pattern di progettazione del comando che definisce il contratto per l’implementazione.
- L’implementazione del ricevitore è separata dall’implementazione del comando.
- Le classi di implementazione del comando scelgono il metodo da invocare sull’oggetto ricevitore, per ogni metodo nel ricevitore ci sarà un’implementazione del comando. Funziona come un ponte tra il ricevitore e i metodi di azione.
- La classe invocatore semplicemente inoltra la richiesta dal client all’oggetto comando.
- Il client è responsabile di istanziare il comando appropriato e l’implementazione del ricevitore e quindi associarli insieme.
- Il client è anche responsabile di istanziare l’oggetto invocatore e associare l’oggetto comando ad esso ed eseguire il metodo di azione.
- Il pattern di progettazione del comando è facilmente estendibile, possiamo aggiungere nuovi metodi di azione nei ricevitori e creare nuove implementazioni del comando senza modificare il codice client.
- Lo svantaggio del pattern di progettazione del comando è che il codice diventa enorme e confuso con un elevato numero di metodi di azione e a causa di così tante associazioni.
Modello di Progettazione Command JDK Esempio
Interfaccia Runnable (java.lang.Runnable) e Swing Action (javax.swing.Action) utilizzano il modello di progettazione command.
Source:
https://www.digitalocean.com/community/tutorials/command-design-pattern