Long Method: Replace Conditional Dispatcher with Command

Continuiamo con lo smell: Long Method

   

Problema:

Logiche condizionali vengono usate per le richieste di dispaccio e per eseguire azioni.

Un esempio di logica errata:

Code Snippet
  1.             if (ac.Name.Equals("ATTACCO"))
  2.                 // ...
  3.             else if (ac.Name.Equals("DIFESA"))
  4.                 // ...
  5.             else if (ac.Name.Equals("BERSERK"))
  6.                 // ...
  7.             else if (ac.Name.Equals("GUARDINGO"))
  8.                 // ...

 
Motivazione:

Molti sistemi ricevono, instradano e lavorano con richieste.
Un conditional-dispatcher è un grosso blocco condizionale (if-else o switch) che si occupa di fare il gestire i messaggi ricevuti.

Le due maggiori motivazioni al refactoring di un conditional-dispatcher con una soluzione basata sul pattern Command sono:

  1. Poca flessibilità a runtime
  2. Uno conditional-dispatcher enorme

Il pattern Command rappresenta un’ottima soluzione per rispondere alla motivazioni precedenti.
Il pattern crea una differenza tra il client e gli oggetti che eseguono le operazioni. Questo pattern è estremamente versatile, supporta:

  • Spedire le request a differenti destinatari
  • Queue, logging e richieste da fiutare
  • Transazioni ad alto livello da operazioni primitive
  • Funzionalità di Redo e Undo

Tenendo conto della regola tenere tutto semplice si potrebbe storcere il naso sull’applicazione di questo pattern come soluzione al nostro problema.
Qual’ora il conditional-dispatcher dovrebbe diventare corposo bisogna ricordare che il pattern Command è facile da implementare, versatile e incredibilmente utile.

Soluzione:

Ecco come si mostrerà il codice di sopra applicando il State:

Code Snippet
  1.     class CommandPattern
  2.     {
  3.         delegate void Invoker();
  4.         static Invoker Execute, Undo, Redo;
  5.         class Command
  6.         {
  7.             public Command(Receiver receiver)
  8.             {
  9.                 Execute = receiver.Action;
  10.                 Redo = receiver.Action;
  11.                 Undo = receiver.Reverse;
  12.             }
  13.         }
  14.         public class Receiver
  15.         {
  16.             string build, oldbuild;
  17.             string s = "some string ";
  18.             public void Action()
  19.             {
  20.                 oldbuild = build;
  21.                 build += s;
  22.                 Console.WriteLine("Receiver is adding " + build);
  23.             }
  24.             public void Reverse()
  25.             {
  26.                 build = oldbuild;
  27.                 Console.WriteLine("Receiver is reverting to " + build);
  28.             }
  29.         }
  30.         static void Main()
  31.         {
  32.             new Command(new Receiver());
  33.             Execute();
  34.             Redo();
  35.             Undo();
  36.             Execute();
  37.             Console.ReadLine();
  38.         }
  39.     }
 
Benefici e non
+ Crea un semplice meccaniscmo per l’esecuzione di diversi comportament in una maniera uniforme.
+ Da la possibilità di cambiare a runtime quali richieste sono gestite e come.
+ Richiede un’implementazione semplice.

- Complica il design se le condizioni sono poche.

E non prendete come scusa: “il mio sistema ormai è troppo evoluto per poterne apportare queste migliorie. E’ troppo tardi.”

Se fosse realmente così non esisterebbe il Refactoring :)

Per questo post ho preso spunto da libro Refactoring To Patterns di Joshua Kerievsky.