 |
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
|
|
 |
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.
This structural code demonstrates the Command pattern which stores requests as
objects allowing clients to execute or playback the requests.
Show code
|
// Command pattern -- Structural example
|
|
using System;
namespace DoFactory.GangOfFour.Command.Structural
{
// MainApp test applicatio
class MainApp
{
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.Read();
}
}
// "Command"
abstract class Command
{
protected Receiver receiver;
// Constructor
public Command(Receiver receiver)
{
this.receiver = receiver;
}
public abstract void Execute();
}
// "ConcreteCommand"
class ConcreteCommand : Command
{
// Constructor
public ConcreteCommand(Receiver receiver) :
base(receiver)
{
}
public override void Execute()
{
receiver.Action();
}
}
// "Receiver"
class Receiver
{
public void Action()
{
Console.WriteLine("Called Receiver.Action()");
}
}
// "Invoker"
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.
Show code
|
// Command pattern -- Real World example
|
|
using System;
using System.Collections;
namespace DoFactory.GangOfFour.Command.RealWorld
{
// MainApp test application
class MainApp
{
static void Main()
{
// Create user and let her compute
User user = new User();
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.Read();
}
}
// "Command"
abstract class Command
{
public abstract void Execute();
public abstract void UnExecute();
}
// "ConcreteCommand"
class CalculatorCommand : Command
{
char @operator;
int operand;
Calculator calculator;
// Constructor
public CalculatorCommand(Calculator calculator,
char @operator, int operand)
{
this.calculator = calculator;
this.@operator = @operator;
this.operand = operand;
}
public char Operator
{
set{ @operator = value; }
}
public int Operand
{
set{ operand = value; }
}
public override void Execute()
{
calculator.Operation(@operator, operand);
}
public override void UnExecute()
{
calculator.Operation(Undo(@operator), operand);
}
// Private helper function
private char Undo(char @operator)
{
char undo;
switch(@operator)
{
case '+': undo = '-'; break;
case '-': undo = '+'; break;
case '*': undo = '/'; break;
case '/': undo = '*'; break;
default : undo = ' '; break;
}
return undo;
}
}
// "Receiver"
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);
}
}
// "Invoker"
class User
{
// Initializers
private Calculator calculator = new Calculator();
private ArrayList commands = new ArrayList();
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++] as Command;
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.
Show code
|
// Command pattern -- .NET optimized
|
See our Singleton page for a .NET optimized code sample.
|
|
|