6
Jan

Pattern: Command

Recentemente mi sono imbattuto per motivi universitari in questo pattern. La sua funzione quale è? cito dal wiki di UgiDotNet [che si ispira da DoFactory]:

Il design pattern Command permette (alla classe Client) di incapsulate una richiesta, ossia un comando (Execute) da eseguire ed i suoi parametri (state), sotto forma di oggetto (ConcreteCommand) da usare per parametrizzare il comportamento di altri oggetti (Invoker) con diverse richieste (ossia con diversi oggetti ConcreteCommand), code di richieste oppure log di richieste.

Con la figura forse risulta più chiaro

Command Pattern

Se ancora non lo fosse provo a spiegarlo a parole mie “ha lo scopo di inglobare/nascondere la sintassi (magari complessa) di un comando dietro una classe e permettere di trasferire l’oggetto tra varie parti del programma per venir eseguito magari altrove (anche fisicamente)“. I più attenti noteranno che le due frasi non coincidono, infatti secondo me la frase del wiki non è corretta anzi contiene pure una errata valutazione di state! Se uso questo pattern, siamo tutti daccordo, è per nascondere/incapsulare una comlessità agli occhi di un client; ora questo comporta il beneficio di poter eseguire una serie di comandi dietro l’invocazione di un metodo generico quale Execute(). Mi spiego meglio se chiedo ad esempio addMoney(300) su una classe Conto mi aspetto che aggiunga 300 al mio conto, se eseguo Money.Execute(); potrei voler eseguire non solo add ma anche remove in una qualunque sequenza. Es:


$money = new Money('banca','username','password');

$money->add(300);

$money->add(100);

$money->remove(50) ;

$money->Execute();

echo $money->State();

Ora i più attenti mi diranno : “Ehi ma allora anche add e remove devono andare nella interfaccia comune di Command“? No,ovviamente. Mentre State come vedete alla fine lo uso per sapere lo stato dell’operazione tipo: training, attending, failed,suspended. Tutto questo è possibile presupponendo che chi usa la classe Money conosca i metodi in aggiunta a quelli dell’interfaccia di Command. Questa non sembra una soluzione acettabile allora posso usare la stessa tecnica usata in SQL dai vari driver in circolazione:


$money = new Money('banca','username','password');

$money->prepare('add','300');

$money->prepare('add','100');

$money->prepare('remove','50');

$money->Execute();

echo $money->State();

L’introduzione di prepare permette di avere un miglior controllo della situazione, semplificando la creazione di Money (meno funzioni da implementare). A questo punto basta sbizarrirsi un pò per vedere i vantaggi di questo pattern: legato al proxy permette di eseguire comandi su macchine remote o utilizzare servizi remoti. Notare quindi come nel grafico UML non venga specificato come il Receiver debba essere implementato ma fornisca uno schema logico ad ampio spettro.
Per la cronaca la mia soluzione è quella che si avvicina di più al GOF usando interfacce e non classi astratte.

PS: una interessante immagine con le relazioni tra patterns.

  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google
  • Technorati
 

2 Responses to “Pattern: Command”

  1. mist3r0 Says:

    Salve a tutti, non ho ben capito il pattern command, ho scritto del codice qui sotto, che dovrebbe rappresentare il patter comman,

    interface Command {
        void execute();
    }
    

    Adesso data la class A

    class A {
        ...
        public void copia() {
           ...
        }
        ...
    }
    

    per trasformare la chiamata del metodo copia in un command, si faccio:

    class ACommand implements Command {
        A a;
        ACommand(A a) {
            this.a = a;
        }
        public void execute() {
            a.copia();
        }
    }
    

    Adesso nella class B si può permettere di eseguire il metodo copia senza conoscere A:

    class B {
        Command c;
        void setCommand(Command c) {
            this.c = c;
        }
        void run() {
            c.execute();
        }
    }
    

    Ora, io mi chiedo, e mi scuso in anticipo per la mia ignoranza, ma se command è un’interfaccia come faccio nella classe B, a dichiarare un command e chiamare su di esso il metodo execute essendo esso non ridefinito e metodo di un’interfaccia????

  2. Yoghi Says:

    Non ho capito molto dal codice che hai scritto spero di rispondere alla domanda cmq.:
    questo pattern lo usiamo per rendere opaca una parte del nostro codice al resto del sistema, ossia diciamo se vuoi eseguire “copia” non ti interessa sapere come e dove (soprattutto) è situato copia tu sai che ci penserà il tuo “Command” ad eseguirlo in modo apporpriato magari con una invocazione remota SOAP (stile “service”).

    Es:

    $A = new A("ip","nomeservizio");
    // solo chi ha scritto A sa realmente cosa succederà
    // quando chiamo A.execute()
    $b = new B();
    $b->setCommand($A);
    $b->execute();
    

    dentro B chiamo A.execute(), o meglio ancora la sequenza di comandi di tutti i “Command” che gli avevo passato in precedenza con il set.
    “c” dentro B è un puntatore alla istanza che passi a setCommand, che basta sia una classe che implementa “Command”; ti ricordo che non posso creare una istanza di una interfaccia, devo sempre istanziare una classe che implementa una interfaccia e posso usare tale classe anche senza sapere come è fatta basta che sappia che interfaccia implementa, in quanto è lo scopo delle interfacce.

    Se sono stato poco chiaro o hai ancora dubbi siamo qui.