Tuesday, April 9, 2013

.NET Design Patterns(14) - Command


Command Design Pattern

 definition
 UML diagram
 participants
 sample code in C#


definition

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

Frequency of use:   medium high

UML class diagram


participants

    The classes and/or 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.

sample code in C#

This structural code demonstrates the Command pattern which stores requests as objects allowing clients to execute or playback the requests.
Hide code 

// Command pattern -- Structural example

using System;

namespace DoFactory.GangOfFour.Command.Structural
{
  /// <summary>
  /// MainApp startup class for Structural
  /// Command Design Pattern.
  /// </summary>
  class MainApp
  {
    /// <summary>
    /// Entry point into console application.
    /// </summary>
    static void Main()
    {
      // Create receiver, command, and invoker
      Receiver receiver = new Receiver();
      Command command = new ConcreteCommand(receiver);
      Invoker invoker = new Invoker();

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

      // Wait for user
      Console.ReadKey();
    }
  }

  /// <summary>
  /// The 'Command' abstract class
  /// </summary>
  abstract class Command
  {
    protected Receiver receiver;

    // Constructor
    public Command(Receiver receiver)
    {
      this.receiver = receiver;
    }

    public abstract void Execute();
  }

  /// <summary>
  /// The 'ConcreteCommand' class
  /// </summary>
  class ConcreteCommand : Command
  {
    // Constructor
    public ConcreteCommand(Receiver receiver) :
      base(receiver)
    {
    }

    public override void Execute()
    {
      receiver.Action();
    }
  }

  /// <summary>
  /// The 'Receiver' class
  /// </summary>
  class Receiver
  {
    public void Action()
    {
      Console.WriteLine("Called Receiver.Action()");
    }
  }

  /// <summary>
  /// The 'Invoker' class
  /// </summary>
  class Invoker
  {
    private Command _command;

    public void SetCommand(Command command)
    {
      this._command = command;
    }

    public void ExecuteCommand()
    {
      _command.Execute();
    }
  }
}

Output
Called Receiver.Action()




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.
Hide code 

// Command pattern -- Real World example

using System;
using System.Collections.Generic;

namespace DoFactory.GangOfFour.Command.RealWorld
{
  /// <summary>
  /// MainApp startup class for Real-World
  /// Command Design Pattern.
  /// </summary>
  class MainApp
  {
    /// <summary>
    /// Entry point into console application.
    /// </summary>
    static void Main()
    {
      // Create user and let her compute
      User user = new User();

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

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

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

      // Wait for user
      Console.ReadKey();
    }
  }

  /// <summary>
  /// The 'Command' abstract class
  /// </summary>
  abstract class Command
  {
    public abstract void Execute();
    public abstract void UnExecute();
  }

  /// <summary>
  /// The 'ConcreteCommand' class
  /// </summary>
  class CalculatorCommand : Command
  {
    private char _operator;
    private int _operand;
    private Calculator _calculator;

    // Constructor
    public CalculatorCommand(Calculator calculator,
      char @operator, int operand)
    {
      this._calculator = calculator;
      this._operator = @operator;
      this._operand = operand;
    }

    // Gets operator
    public char Operator
    {
      set { _operator = value; }
    }

    // Get operand
    public int Operand
    {
      set { _operand = value; }
    }

    // Execute new command
    public override void Execute()
    {
      _calculator.Operation(_operator, _operand);
    }

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

    // Returns opposite operator for given operator
    private char Undo(char @operator)
    {
      switch (@operator)
      {
        case '+'return '-';
        case '-'return '+';
        case '*'return '/';
        case '/'return '*';
        defaultthrow new
         ArgumentException("@operator");
      }
    }
  }

  /// <summary>
  /// The 'Receiver' class
  /// </summary>
  class Calculator
  {
    private int _curr = 0;

    public void Operation(char @operator, int operand)
    {
      switch (@operator)
      {
        case '+': _curr += operand; break;
        case '-': _curr -= operand; break;
        case '*': _curr *= operand; break;
        case '/': _curr /= operand; break;
      }
      Console.WriteLine(
        "Current value = {0,3} (following {1} {2})",
        _curr, @operator, operand);
    }
  }

  /// <summary>
  /// The 'Invoker' class
  /// </summary>
  class User
  {
    // Initializers
    private Calculator _calculator = new Calculator();
    private List<Command> _commands = new List<Command>();
    private int _current = 0;

    public void Redo(int levels)
    {
      Console.WriteLine("\n---- Redo {0} levels ", levels);
      // Perform redo operations
      for (int i = 0; i < levels; i++)
      {
        if (_current < _commands.Count - 1)
        {
          Command command = _commands[_current++];
          command.Execute();
        }
      }
    }

    public void Undo(int levels)
    {
      Console.WriteLine("\n---- Undo {0} levels ", levels);
      // Perform undo operations
      for (int i = 0; i < levels; i++)
      {
        if (_current > 0)
        {
          Command command = _commands[--_current] as Command;
          command.UnExecute();
        }
      }
    }

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

      // Add command to undo list
      _commands.Add(command);
      _current++;
    }
  }
}

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)




This .NET optimized code demonstrates the same real-world situation as above but uses modern, built-in .NET features, such as, generics, reflection, object initializers, automatic properties, etc.

No comments:

Post a Comment