The Command design pattern encapsulates a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
A visualization of the classes and objects participating in this pattern.
The classes and objects participating in this pattern include:
Command
)
CalculatorCommand
)
CommandApp
)
User
)
Calculator
)
This structural code demonstrates the Command pattern which stores requests as objects allowing clients to execute or playback the requests.
using System;
namespace Command.Structural
{
/// <summary>
/// Command Design Pattern
/// </summary>
public class Program
{
public static void Main(string[] args)
{
// 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>
public abstract class Command
{
protected Receiver receiver;
// Constructor
public Command(Receiver receiver)
{
this.receiver = receiver;
}
public abstract void Execute();
}
/// <summary>
/// The 'ConcreteCommand' class
/// </summary>
public class ConcreteCommand : Command
{
// Constructor
public ConcreteCommand(Receiver receiver) :
base(receiver)
{
}
public override void Execute()
{
receiver.Action();
}
}
/// <summary>
/// The 'Receiver' class
/// </summary>
public class Receiver
{
public void Action()
{
Console.WriteLine("Called Receiver.Action()");
}
}
/// <summary>
/// The 'Invoker' class
/// </summary>
public class Invoker
{
Command command;
public void SetCommand(Command command)
{
this.command = command;
}
public void ExecuteCommand()
{
command.Execute();
}
}
}
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.
using System;
using System.Collections.Generic;
namespace Command.RealWorld
{
/// <summary>
/// Command Design Pattern
/// </summary>
public class Program
{
public static void Main(string[] args)
{
// 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>
public abstract class Command
{
public abstract void Execute();
public abstract void UnExecute();
}
/// <summary>
/// The 'ConcreteCommand' class
/// </summary>
public 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;
}
// 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 '*';
default:
throw new
ArgumentException("@operator");
}
}
}
/// <summary>
/// The 'Receiver' class
/// </summary>
public class Calculator
{
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>
public class User
{
// Initializers
Calculator calculator = new Calculator();
List<Command> commands = new List<Command>();
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++;
}
}
}
The .NET optimized code demonstrates the
same real-world situation as above but uses modern, built-in .NET features,
such as, generics, reflection, LINQ, lambda functions, etc.
You can find an example on our Singleton pattern page.
All other patterns (and much more) are available in our Dofactory .NET product.
Not only does Dofactory .NET cover the Gang of Four and Enterprise patterns, it also includes
pattern architectures, low-code, and RAD (Rapid Application Development) techniques.
Accelerate development to where you can write
entire solutions in just 33 days!.
This unique package will change your developer lifestyle.
Here's what is included: