Wednesday, October 14, 2015

Improve the knowledge about Command Design Patterns

Definition

Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.




UML class diagram







Participants


    The classes and objects participating in this pattern are:
  • Command  (Command)
    • declares an interface for executing an operation
  • ConcreteCommand  (CalculatorCommand)
    • defines a binding between a Receiver object and an action
    • implements Execute by invoking the corresponding operation(s) on Receiver
  • Client  (CommandApp)
    • creates a ConcreteCommand object and sets its receiver
  • Invoker  (User)
    • asks the command to carry out the request
  • Receiver  (Calculator)
    • knows how to perform the operations associated with carrying out the request.



Structural code in C#


This structural code demonstrates the Command pattern which stores requests as objects allowing clients to execute or playback the requests.
  1. using System;

  2. namespace DoFactory.GangOfFour.Command.Structural
  3. {
  4.   /// <summary>
  5.   /// MainApp startup class for Structural
  6.   /// Command Design Pattern.
  7.   /// </summary>
  8.   class MainApp
  9.   {
  10.     /// <summary>
  11.     /// Entry point into console application.
  12.     /// </summary>
  13.     static void Main()
  14.     {
  15.       // Create receiver, command, and invoker
  16.       Receiver receiver = new Receiver();
  17.       Command command = new ConcreteCommand(receiver);
  18.       Invoker invoker = new Invoker();

  19.       // Set and execute command
  20.       invoker.SetCommand(command);
  21.       invoker.ExecuteCommand();

  22.       // Wait for user
  23.       Console.ReadKey();
  24.     }
  25.   }

  26.   /// <summary>
  27.   /// The 'Command' abstract class
  28.   /// </summary>
  29.   abstract class Command
  30.   {
  31.     protected Receiver receiver;

  32.     // Constructor
  33.     public Command(Receiver receiver)
  34.     {
  35.       this.receiver = receiver;
  36.     }

  37.     public abstract void Execute();
  38.   }

  39.   /// <summary>
  40.   /// The 'ConcreteCommand' class
  41.   /// </summary>
  42.   class ConcreteCommand : Command
  43.   {
  44.     // Constructor
  45.     public ConcreteCommand(Receiver receiver) :
  46.       base(receiver)
  47.     {
  48.     }

  49.     public override void Execute()
  50.     {
  51.       receiver.Action();
  52.     }
  53.   }

  54.   /// <summary>
  55.   /// The 'Receiver' class
  56.   /// </summary>
  57.   class Receiver
  58.   {
  59.     public void Action()
  60.     {
  61.       Console.WriteLine("Called Receiver.Action()");
  62.     }
  63.   }

  64.   /// <summary>
  65.   /// The 'Invoker' class
  66.   /// </summary>
  67.   class Invoker
  68.   {
  69.     private Command _command;

  70.     public void SetCommand(Command command)
  71.     {
  72.       this._command = command;
  73.     }

  74.     public void ExecuteCommand()
  75.     {
  76.       _command.Execute();
  77.     }
  78.   }
  79. }
  80.  
  81.  

Output
Called Receiver.Action()





Real-world code in C#


This real-world code demonstrates the Command pattern used in a simple calculator with unlimited number of undo's and redo's. Note that in C#  the word 'operator' is a keyword. Prefixing it with '@' allows using it as an identifier.
  1. using System;
  2. using System.Collections.Generic;

  3. namespace DoFactory.GangOfFour.Command.RealWorld
  4. {
  5.   /// <summary>
  6.   /// MainApp startup class for Real-World
  7.   /// Command Design Pattern.
  8.   /// </summary>
  9.   class MainApp
  10.   {
  11.     /// <summary>
  12.     /// Entry point into console application.
  13.     /// </summary>
  14.     static void Main()
  15.     {
  16.       // Create user and let her compute
  17.       User user = new User();

  18.       // User presses calculator buttons
  19.       user.Compute('+', 100);
  20.       user.Compute('-', 50);
  21.       user.Compute('*', 10);
  22.       user.Compute('/', 2);

  23.       // Undo 4 commands
  24.       user.Undo(4);

  25.       // Redo 3 commands
  26.       user.Redo(3);

  27.       // Wait for user
  28.       Console.ReadKey();
  29.     }
  30.   }

  31.   /// <summary>
  32.   /// The 'Command' abstract class
  33.   /// </summary>
  34.   abstract class Command
  35.   {
  36.     public abstract void Execute();
  37.     public abstract void UnExecute();
  38.   }

  39.   /// <summary>
  40.   /// The 'ConcreteCommand' class
  41.   /// </summary>
  42.   class CalculatorCommand : Command
  43.   {
  44.     private char _operator;
  45.     private int _operand;
  46.     private Calculator _calculator;

  47.     // Constructor
  48.     public CalculatorCommand(Calculator calculator,
  49.       char @operator, int operand)
  50.     {
  51.       this._calculator = calculator;
  52.       this._operator = @operator;
  53.       this._operand = operand;
  54.     }

  55.     // Gets operator
  56.     public char Operator
  57.     {
  58.       set { _operator = value; }
  59.     }

  60.     // Get operand
  61.     public int Operand
  62.     {
  63.       set { _operand = value; }
  64.     }

  65.     // Execute new command
  66.     public override void Execute()
  67.     {
  68.       _calculator.Operation(_operator, _operand);
  69.     }

  70.     // Unexecute last command
  71.     public override void UnExecute()
  72.     {
  73.       _calculator.Operation(Undo(_operator), _operand);
  74.     }

  75.     // Returns opposite operator for given operator
  76.     private char Undo(char @operator)
  77.     {
  78.       switch (@operator)
  79.       {
  80.         case '+': return '-';
  81.         case '-': return '+';
  82.         case '*': return '/';
  83.         case '/': return '*';
  84.         default: throw new
  85.          ArgumentException("@operator");
  86.       }
  87.     }
  88.   }

  89.   /// <summary>
  90.   /// The 'Receiver' class
  91.   /// </summary>
  92.   class Calculator
  93.   {
  94.     private int _curr = 0;

  95.     public void Operation(char @operator, int operand)
  96.     {
  97.       switch (@operator)
  98.       {
  99.         case '+': _curr += operand; break;
  100.         case '-': _curr -= operand; break;
  101.         case '*': _curr *= operand; break;
  102.         case '/': _curr /= operand; break;
  103.       }
  104.       Console.WriteLine(
  105.         "Current value = {0,3} (following {1} {2})",
  106.         _curr, @operator, operand);
  107.     }
  108.   }

  109.   /// <summary>
  110.   /// The 'Invoker' class
  111.   /// </summary>
  112.   class User
  113.   {
  114.     // Initializers
  115.     private Calculator _calculator = new Calculator();
  116.     private List<Command> _commands = new List<Command>();
  117.     private int _current = 0;

  118.     public void Redo(int levels)
  119.     {
  120.       Console.WriteLine("\n---- Redo {0} levels ", levels);
  121.       // Perform redo operations
  122.       for (int i = 0; i < levels; i++)
  123.       {
  124.         if (_current < _commands.Count - 1)
  125.         {
  126.           Command command = _commands[_current++];
  127.           command.Execute();
  128.         }
  129.       }
  130.     }

  131.     public void Undo(int levels)
  132.     {
  133.       Console.WriteLine("\n---- Undo {0} levels ", levels);
  134.       // Perform undo operations
  135.       for (int i = 0; i < levels; i++)
  136.       {
  137.         if (_current > 0)
  138.         {
  139.           Command command = _commands[--_current] as Command;
  140.           command.UnExecute();
  141.         }
  142.       }
  143.     }

  144.     public void Compute(char @operator, int operand)
  145.     {
  146.       // Create command operation and execute it
  147.       Command command = new CalculatorCommand(
  148.         _calculator, @operator, operand);
  149.       command.Execute();

  150.       // Add command to undo list
  151.       _commands.Add(command);
  152.       _current++;
  153.     }
  154.   }
  155. }
  156.  
  157.  

Output
Current value = 100 (following + 100)
Current value =  50 (following - 50)
Current value = 500 (following * 10)
Current value = 250 (following / 2)

---- Undo 4 levels
Current value = 500 (following * 2)
Current value =  50 (following / 10)
Current value = 100 (following + 50)
Current value =   0 (following - 100)

---- Redo 3 levels
Current value = 100 (following + 100)
Current value =  50 (following - 50)
Current value = 500 (following * 10)

No comments:

Post a Comment