Home > GOF Pattern, java > GoF Patterns: Command

GoF Patterns: Command

8 novembre 2011

Translate in English with Google Translate
In questo articolo tratterò il pattern Command anche detto Action o Transaction

Motivazione

Si tratta di un pattern comportamentale basato su oggetti e viene utilizzato quando si ha la necessità di disaccoppiare l’invocazione di un comando dai suoi dettagli implementativi, separando colui che invoca il comando da colui che esegue l’operazione.

Tale operazione viene realizzata attraverso questa catena: Client->Invocatore->Ricevitore

  1. Il Client non è tenuto a conoscere i dettagli del comando ma il suo compito è solo quello di chiamare il metodo dell’ Invocatore che si occuperà di intermediare l’operazione.
  2. L’Invocatore ha l’obiettivo di incapsulare, nascondere i dettagli della chiamata come nome del metodo e parametri.
  3. Il Ricevitore utilizza i parametri ricevuti per eseguire l’operazione


Ma tra l’Invocatore ed il Ricevitore viene posto il Command ossia il comando da eseguire. Il Command è una semplice interfaccia che viene implementata da una o più classi concrete che invocano il Receiver.

Partecipanti e Struttura
Questo pattern è composto dai seguenti partecipanti:

  1. Client: colui che richiede il comando ed imposta il Receiver
  2. Invoker: colui che effettua l’invocazione del comando
  3. Command: interfaccia generica per l’esecuzione del comando
  4. ConcreteCommand: implementazione del comando che consente di collegare l’Invoker con il Receiver
  5. Receiver: colui che riceve il comando e sa come eseguirlo.
Command Pattern

Command Pattern

Conseguenze

Tale pattern presenta i seguenti vantaggi/svantaggi:

  1. riduce l’accoppiamento: il Command disaccoppia l’Invoker dal Receiver, ossia colui che invoca da colui che esegue facendo in modo che i dettagli implementativi siano a conoscenza solo del Receiver.
  2. facile estendibilità: è possibile aggiungere facilmente nuovi comandi implementando l’interfaccia Command

Implementazione

Come esempio pensiamo al caso in cui vogliamo listare i files presenti in una cartella, quindi su Windows si tratta di eseguire il comando MSDOS dir c:\

Vediamo come si presenta il pattern in UML con il Class Diagram in base all’esempio:

List Command

List Command

Creiamo la classe InvokerList che definisce:

  1. il costruttore con il parametro Command che viene utilizzato per incapsulare il comando da eseguire
  2. il metodo list che esegue il metodo utilizzato per l’esecuzione del comando
package patterns.command;

public class InvokerList {

    Command command = null;

    public InvokerList(Command command) {
        this.command = command;
    }

    public void list() {
        command.execute();
    }

}

Definiamo l’interfaccia Command che definisce il metodo del comando da eseguire:

package patterns.command;

public interface Command {

    void execute();

}

Definiamo la classe CommandList che implementa l’interfaccia e definisce il comportamenteo del metodo execute:

package patterns.command;

public class CommandList implements Command {

    private Receiver receiver = null;

    public CommandList(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.list();
    }

}

Definiamo la classe Receiver che esegue materialmente il comando:

package patterns.command;

import java.io.InputStream;

public class Receiver {

    public void list() {
        System.out.println( exec( "dir c:\\" ) );
    }

    private String exec(String command) {
        String outString = null;
        try {
            Runtime runtime = Runtime.getRuntime();
            Process process = runtime.exec( "cmd /C " + command );
            process.waitFor();
            InputStream is = process.getInputStream();
            int full = is.available();
            byte[] out = new byte[full];
            is.read(out, 0, (full - 1));
            outString = new String(out);
        } catch (Exception ex1) {
            ex1.printStackTrace();
        }
        return outString;
    }

}

Definiamo la classe Client che invoca l’esecuzione:

package patterns.command;

public class Client {

    public static void main(String[] args) {
        InvokerList invokerList = new InvokerList( new CommandList( new Receiver() ) );
        invokerList.list();
    }

}

L’output del Client è il seguente:

%JAVA_HOME%/bin/java patterns.command.Client
 Il volume nell'unita' C e' ACER
 Numero di serie del volume: 1CFD-DA3D

 Directory di c:\

28/06/2011 20.06 Documents and Settings
29/06/2011 01.23 I386
29/06/2011 01.27 Program Files
07/11/2011 23.09 WINDOWS
9 File 727.881 byte
4 Directory 3.714.228.224 byte disponibili

Estensione dell’esempio precedente
Adesso aggiungiamo un altro comando, abbiamo bisogno di sapere qual’è il path corrente, quindi su windows dobbiamo eseguire il comando dir

Vediamo come si presenta il pattern in UML con il Class Diagram a seguito di questa modifica:

List Path Command

List Path Command

Creiamo la classe InvokerPath che si occupa di invocare il nuovo comando tramite il metodo path:

package patterns.command;

public class InvokerPath {

    Command command = null;

    public InvokerPath(Command command) {
        this.command = command;
    }

    public void path() {
        command.execute();
    }

}

Creiamo la classe CommandPath che si occupa di implementare l’interfaccia Command e realizzare il metodo execute:

package patterns.command;

public class CommandPath implements Command {

    private Receiver receiver = null;

    public CommandPath(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.path();
    }

}

Inserire nella classe Receiver il nuovo metodo path che implementa materialmente il comando:

package patterns.command;

import java.io.InputStream;

public class Receiver {

    public void list() {
        System.out.println( exec( "dir c:\\" ) );
    }

    public void path() {
        System.out.println( exec( "cd" ) );
    }

    private String exec(String command){
        String outString = null;
        try {
            Runtime runtime = Runtime.getRuntime();
            Process process = runtime.exec( "cmd /C " + command );
            process.waitFor();
            InputStream is = process.getInputStream();
            int full = is.available();
            byte[] out = new byte[full];
            is.read(out, 0, (full - 1));
            outString = new String(out);
        } catch (Exception ex1) {
            ex1.printStackTrace();
        }
        return outString;
    }

}

Adesso possiamo invocare dalla nostra classe Client il nuovo comando:

package patterns.command;

public class Client {

    public static void main(String[] args) {
        InvokerPath invokerPath = new InvokerPath( new CommandPath( new Receiver() ) );
        invokerPath.path();
    }

}

L’output è il seguente:

%JAVA_HOME%/bin/java patterns.command.Client
C:\__ARCHIVIO__\project\netbeans\patterns
Categorie:GOF Pattern, java
  1. Omar
    12 gennaio 2012 alle 4:31 PM

    Sono rimasto un po’ perplesso dal tuo esempio. Il succo del command pattern non è quello di scindere l’invocazione del comando dal comando in sè?
    Quindi sarebbe più giusto avere un solo Invoker che gestisca ogni command si voglia creare. In questo modo il programmatore si può concentrare solo sulla logica che il command deve implementare e, volendo, gestire a runtime l’invocazione di tutti o solo una parte dei command secondo le necessità, passando una lista di comandi all’invoker.
    Se per ogni nuovo command andiamo a creare anche un nuovo invoker andiamo proprio contro la riusabilità e manutenibilità del codice, alla base dei design pattern.

    • 14 gennaio 2012 alle 5:59 PM

      sono daccordo con le tue osservazioni ma una moltitudine di Invoker non violano il Pattern Command. L’Invoker ed il Receiver sono disaccoppiati grazie alla presenza del Command che funge da anello di collegamento tra le richieste ed i comandi da eseguire.
      Ovviamente le richieste posso essere diverse e composite pertanto si può integrare il Command con altri pattern, questo per evitare il problema di cui parlavi.
      Nel primo caso, se occorre eseguire comandi diversi, per rendere più flessibile l’invocazione si può usare una Factory mentre nel secondo caso, se occorre inviare una lista di comandi, si può utilizzare il Pattern Composite.
      Oltretutto si potrebbe anche evitare di usare ulteriori pattern. Nel primo caso per esempio si potrebbe semplicemente modificare la classe Invoker in modo da renderla più generica: rinominando il metodo list() in execute() e creando un metodo set(Command command) che detenga l’instanza del Command da eseguire. Un esempio in c# qui.
      Avrei potuto parametrizzare la creazione di nuovi comandi usando il polimorfismo come nell’esempio del link, giusto, ma la mia attenzione era posta sui passaggi successivi più che sull’Invoker pertanto ho semplificato creando una nuova classe Invoker. Non ho invece modificato la classe Invoker originale per aggiungere l’altro metodo path() relativo al nuovo comando in quanto avrei violato il principio Open Closed.
      Mentre invece nel secondo caso per esempio si potrebbe creare una classe MacroCommand che implementi l’interfaccia Command ed abbia come costruttore MacroCommand(Command [] command) che accetti una serie di comandi da eseguire pertanto io eviterei di passare una lista di comandi all’Invoker ma utilizzerei la classe MacroCommand usata da Invoker per eseguire i comandi. Usando MacroCommand si irrigidirebbe di più il sistema ma si avrebbe maggior controllo in merito alla composizione dei comandi. Un esempio in c++ qui.
      Invece in merito alla logica di implementazione del comando, la struttura del pattern prevede che questa risieda nel Receiver ma per evitare una ulteriore proliferazione di classi, è anche possibile definire l’implementazione nella classe concreta del Command. Ovviamente occorre valutare caso per caso. Qualora invece si voglia utilizzare i Receiver, potrebbe essere necessario creare una interfaccia IReceiver in modo tale da consentire l’uso del polimorfismo nell’invocazione del metodo execute() dei Receiver.
      Probabilmente potrebbe essere interessante estendere l’articolo per proporre esempi variegati di questo pattern.

  2. Francesco
    27 luglio 2013 alle 10:35 AM

    Nel secondo esempio, per creare un nuovo comando hai dovuto: creare un altro Invoker (InvokerPath); creare, giustamente, un nuovo Command (CommandPath); aggiungere un metodo a Receiver. Queste modifiche, a loro volta, costringono ad effettuarne altre nel Client: InvokerPath, 2 volte nella 1a riga; CommandPath nel costruttore; chiamata metodo path() nella 2a riga. Dunque almeno 4 modifiche in sole 2 righe di codice per il solo Client (creazioni suddette a parte).
    L’Invoker è generale, dunque è riusabile; è sufficiente implementare il nuovo Command (come hai fatto) e aggiungere il relativo metodo ‘realizzativo’ nel Receiver (il path()).
    Nel Client va solo specificato il Command nel costruttore del generico Invoker e chiamare un suo generico metodo (es. ‘esegui()’), riducendo le modifiche ad una soltanto (nella prima riga).

  3. Francesco
    28 luglio 2013 alle 10:19 PM

    Non è necessario creare un nuovo Invoker per ogni nuovo comando. Ciò, infatti, costringe ad effettuare più modifiche al Client quando ne basterebbe una sola: implementare un nuovo ConcreteCommand che chiami il metodo ‘realizzativo’ da aggiungere nel Receiver.
    Mi rendo conto, tuttavia, che l’esempio è puramente didattico.

    • 29 luglio 2013 alle 7:38 AM

      Probabilmente i tuoi dubbi sono gli stessi presenti nel commento precedente (Omar), relativi alla moltitudine di Invoker.
      Ma questo problema è gestibile, come suggerisci anche tu, creando una classe Invoker generica con la presenza del metodo esegui().

      La classe Invoker verrebbe generalizzata:

      package patterns.command;
      public class Invoker {
          private Command command = null;
          public Invoker(Command command) {
              this.command = command;
          }
          public void esegui() {
              command.execute();
          }
      }
      

      Nel client, come suggerisci, verrebbe richiamato il metodo “esegui()” per tutti i command.

      package patterns.command;
      public class Client {
          public static void main(String[] args) {
              Invoker invoker = new Invoker( new CommandList( new Receiver() ) );
              invoker.esegui();
          }
      }
      

      Sono daccordo con questa gestione, tanto da indicarla come variante nel mio commento precedente.

      Per evitare di modificare la classe esistente InvokerList, considerando che non sempre si può fare e violerebbe il principio Open-Closed, ho introdotto la classe InvokerPath. Su progetto questa modifica richiederebbe un refactoring del codice infatti spesso si trovano delle implementazioni parziali di pattern ma il refactoring non è sempre facile nei tempi disponibili, in questo caso sarebbe fattibile.

      Devo dire che questo pattern è un po’ particolare, nel senso che non prevede un esplicita connessione tra Client ed Invoker pertanto l’Invoker potrebbe anche non essere richiamato dal Client (link) ma da un terzo attore, per esempio il Client potrebbe passare l’istanza del ConcreteCommand ad una coda JMS che si occupi poi di gestire l’invocazione.
      Class estratto dal GoF

      Sequence estratta dal libro GoF

  4. Giorgio
    12 novembre 2014 alle 6:14 PM

    Ciao Giuseppe,

    grazie per i tuoi articoli, leggendo varia altri articoli, ho visto che il pattern Command, viene implementato anche per la gestione del multithreading attraverso l’interfaccia Runnable, in tal caso quali sono secondo te gli attori principali per poter implementare tale pattern?

    Secondo me dovrebbe essere:

    Command -> Runnable interface
    ConcreteCommand -> Runnable implementation
    Invoker -> Classe Thread
    Receiver-> Oggetto che effettua la business logic nel concreteCommand, che in tal caso non c’è

    // Command 
    public interface Runnable(){
        
        public void run()
    }
    
    // ConcreteCommand 
    public class HeavyWorkRunnable implements Runnable {
     
        @Override
        public void run() {
            System.out.println("Doing heavy processing - START "+Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
                //Get database connection, delete unused data from DB
                doDBProcessing();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Doing heavy processing - END "+Thread.currentThread().getName());
        }
     
        private void doDBProcessing() throws InterruptedException {
            Thread.sleep(5000);
        }
     
    }
    
    //Client
    public class ThreadRunExample {
     
        public static void main(String[] args){ 
            //Invokers	
            Thread t1 = new Thread(new HeavyWorkRunnable(), "t1");
            Thread t2 = new Thread(new HeavyWorkRunnable(), "t2");
            System.out.println("Starting Runnable threads");
            t1.start();
            t2.start();         
        }
    }
    

    Secondo te? Io non sono sicuro, perchè secondo il pattern, il Receiver dovrebbe essere passato come parametro al ConcreteCommand, ma nel mio esempio sopra non avviene questa cosa.
    Ti faccio questa domanda perchè sto cercando di capire dove viene applicato questo pattern nella Java API, in modo da ricordarmi meglio il suo utilizzo.

    Ciao,
    Grazie ancora,
    Giorgio.

  5. Giorgio
    12 novembre 2014 alle 8:36 PM

    Mi potresti fornire altri esempi di Command pattern utilizzati nella java API oltre a quelli classici di java swing e dei threads?

    Grazie in anticipo,
    Giorgio.

  1. No trackbacks yet.
I commenti sono chiusi.
%d blogger cliccano Mi Piace per questo: