Summary: in this tutorial, you’ll learn about the C# Command design pattern and how to use it to make your application more flexible and extensible.
Introduction to the C# command design pattern
The Command pattern is a behavioral design pattern that encapsulates a request as an object, thereby allowing the requester to be decoupled from the object that operates.
The Command pattern encapsulates the request as a command object that contains all the necessary information to execute the request.
The requester doesn’t need to know the details of how the command is executed. Instead, it simply requests the execution of the command by calling its execute method.
This makes your application more flexible and extensible because the requester can easily switch out different commands without the need of understanding how those commands are executed.
Because the command object stores the necessary information to reverse or replay the operation, the Command pattern enables features like undo and redo.
C# Command pattern structure
The following UML diagram illustrates the Command design pattern:
In this diagram:
ICommand: Defines the interface for executing an operation.ConcreteCommand: Implements theICommandinterface and defines the binding between aReceiverobject and an action. TheConcreteCommandimplements theExecute()andUndo()method by calling the corresponding operations on theReceiverobject.- Client: Creates
ConcreteCommandobjects and sets their receivers. - Receiver: Knows how to perform the operations associated with carrying out a request.
- Invoker: Asks the command to carry out the request.
Collaboration between participants:
- The
Clientcreates aConcreteCommandobject and tells it which receiver object should receive the command. - An
Invokerobject saves theConcreteCommandobject. - The
Invokercalls theExecutemethod on theICommandobject to carry out the request. If the command is undoable, theConcreteCommandobject stores information needed to undo the command before callingExecutemethod. - The
ConcreteCommandobject then directs theReceiverto perform the necessary actions to fulfill the request.
The following shows how to implement the command pattern in C# (without Undo() method, more on this in the next example):
namespace CommandDesignPattern;
public interface ICommand
{
void Execute();
}
public class Receiver
{
public void Action()
{
Console.WriteLine("Perform an action");
}
}
public class ConcreteCommand : ICommand
{
private Receiver Receiver
{
get; set;
}
public ConcreteCommand(Receiver receiver)
{
Receiver = receiver;
}
public void Execute() => Receiver.Action();
}
public class Invoker
{
public ICommand? Command
{
get; set;
}
public void Invoke() => Command?.Execute();
}
class Client
{
public static void Main(string[] args)
{
var receiver = new Receiver();
var concreteCommand = new ConcreteCommand(receiver);
var invoker = new Invoker
{
Command = concreteCommand
};
invoker.Invoke();
}
}Code language: C# (cs)C# Command pattern example
The following program defines a Calculator class with methods for basic arithmetic operations including adding, subtracting, multiplying, and dividing that update the current value.
In the Main() method of the Program class, we create an instance of the Calculator class, add 20 to the current value, subtract 5 from it, and then display the current value of the calculator in the console:
namespace CommandPattern;
public class Calculator
{
public double CurrentValue
{
get; private set;
}
public double Add(double valueToAdd) => CurrentValue += valueToAdd;
public double Subtract(double valueToSubtract) => CurrentValue -= valueToSubtract;
public double Divide(double valueToDivide) => CurrentValue /= valueToDivide;
public double Mutiply(double valueToMultiply) => CurrentValue *= valueToMultiply;
}
public class Program
{
public static void Run(string[] args)
{
var calculator = new Calculator();
calculator.Add(20);
calculator.Subtract(5);
Console.WriteLine(calculator.CurrentValue);
}
}Code language: C# (cs)Output:
15Code language: C# (cs)The program currently doesn’t support the undo feature because it is not using the Command pattern. To add the undo feature, we can refactor the program by applying the Command pattern:
namespace CommandPattern;
public interface ICommand
{
double Execute(double value);
double Undo(double value);
}
public class AddCommand : ICommand
{
private readonly double _valueToAdd;
public AddCommand(double valueToAdd)
{
_valueToAdd = valueToAdd;
}
public double Execute(double currentValue) => currentValue += _valueToAdd;
public double Undo(double currentValue) => currentValue -= _valueToAdd;
}
public class SubtractCommand : ICommand
{
private readonly double _valueToSubtract;
public SubtractCommand(double valueToSubtract)
{
_valueToSubtract = valueToSubtract;
}
public double Execute(double currentValue) => currentValue -= _valueToSubtract;
public double Undo(double currentValue) => currentValue += _valueToSubtract;
}
public class DivideCommand : ICommand
{
private readonly double _valueToDivide;
public DivideCommand(double valueToDivide)
{
_valueToDivide = valueToDivide;
}
public double Execute(double currentValue) => currentValue /= _valueToDivide;
public double Undo(double currentValue) => currentValue *= _valueToDivide;
}
public class MultiplyCommand : ICommand
{
private readonly double _valueToMultiply;
public MultiplyCommand(double valueToMultiply)
{
_valueToMultiply = valueToMultiply;
}
public double Execute(double currentValue) => currentValue *= _valueToMultiply;
public double Undo(double currentValue) => currentValue /= _valueToMultiply;
}
public class Calculator
{
public double CurrentValue
{
get; private set;
}
public Stack<ICommand> _commandHistory = new();
public void ExecuteCommand(ICommand command)
{
CurrentValue = command.Execute(CurrentValue);
_commandHistory.Push(command);
}
public void Undo()
{
var command = _commandHistory.Pop();
CurrentValue = command.Undo(CurrentValue);
}
}
public class Program
{
public static void Main(string[] args)
{
var calculator = new Calculator();
calculator.ExecuteCommand(new AddCommand(20));
calculator.ExecuteCommand(new SubtractCommand(10));
calculator.ExecuteCommand(new MultiplyCommand(5));
Console.WriteLine(calculator.CurrentValue);
calculator.Undo();
Console.WriteLine(calculator.CurrentValue);
}
}Code language: C# (cs)How it works.
First, create an ICommand interface that has two methods Execute and Undo:
public interface ICommand
{
double Execute(double value);
double Undo(double value);
}Code language: C# (cs)Second, define the AddCommand that implements the ICommand interface. The AddCommand adds a value to a current value:
public class AddCommand : ICommand
{
private readonly double _valueToAdd;
public AddCommand(double valueToAdd)
{
_valueToAdd = valueToAdd;
}
public double Execute(double currentValue) => currentValue += _valueToAdd;
public double Undo(double currentValue) => currentValue -= _valueToAdd;
}Code language: C# (cs)The Execute() method adds a value to the current value. And the Undo() method subtracts the same value from the current value.
Similarly, define the SubtractCommand, MultiplyCommand, and DivideCommand classes:
public class SubtractCommand : ICommand
{
private readonly double _valueToSubtract;
public SubtractCommand(double valueToSubtract)
{
_valueToSubtract = valueToSubtract;
}
public double Execute(double currentValue) => currentValue -= _valueToSubtract;
public double Undo(double currentValue) => currentValue += _valueToSubtract;
}
public class DivideCommand : ICommand
{
private readonly double _valueToDivide;
public DivideCommand(double valueToDivide)
{
_valueToDivide = valueToDivide;
}
public double Execute(double currentValue) => currentValue /= _valueToDivide;
public double Undo(double currentValue) => currentValue *= _valueToDivide;
}
public class MultiplyCommand : ICommand
{
private readonly double _valueToMultiply;
public MultiplyCommand(double valueToMultiply)
{
_valueToMultiply = valueToMultiply;
}
public double Execute(double currentValue) => currentValue *= _valueToMultiply;
public double Undo(double currentValue) => currentValue /= _valueToMultiply;
}Code language: C# (cs)Third, define the Calculator class that executes a command and undoes it:
public class Calculator
{
public double CurrentValue
{
get; private set;
}
public Stack<ICommand> _commandHistory = new();
public void ExecuteCommand(ICommand command)
{
CurrentValue = command.Execute(CurrentValue);
_commandHistory.Push(command);
}
public void Undo()
{
var command = _commandHistory.Pop();
CurrentValue = command.Undo(CurrentValue);
}
}Code language: C# (cs)The Calculator class has a CurrentValue property that represents its current value. Also, it has a private field _commandHistory that keeps track of previously executed commands.
The ExecuteCommand method takes an object. The method calls the ICommandExecute() method of an object with the current value of the calculator and then adds the ICommand object to the command history stack.ICommand
The Undo method pops the most recent command off of the command history stack, calls the Undo method of the command with the current value of the calculator, and updates the CurrentValue property with the result.
Finally, define the Program class that demonstrates the ability to execute and undo commands using the Command pattern:
public class Program
{
public static void Main(string[] args)
{
var calculator = new Calculator();
calculator.ExecuteCommand(new AddCommand(20)); // -> 20
calculator.ExecuteCommand(new SubtractCommand(10)); // -> 10
calculator.ExecuteCommand(new MultiplyCommand(5)); // -> 50
Console.WriteLine(calculator.CurrentValue); // output 50
calculator.Undo(); // 10
Console.WriteLine(calculator.CurrentValue); // output 10
}
}Code language: C# (cs)Output:
50
10Code language: C# (cs)In the Main() method:
- First, create a new instance of the
Calculatorclass. - Next, execute three commands including
AddCommand,SubtractCommand, andMultiplyCommandusing theExecuteCommandmethod of theCalculatorobject with the appropriate arguments (20, 10, and 5, respectively). - Then, display the
CurrentValueof theCalculatorobject to the console. - After that, call the
Undomethod of theCalculatorobject, which undoes the most recent command (theMultiplyCommandin this case) and updates theCurrentValueproperty of theCalculatorobject with the result of the undo operation. - Finally, print the updated current value of the calculator to the console.
In this program, the following objects map to the Command pattern:
ICommandinterface: defines the common methods that all concrete commands should implement.AddCommand,SubtractCommand,MultiplyCommand, andDivideCommandclasses: concrete commands that implement theICommandinterface and encapsulate the operations that can be executed on theCurrentValueproperty of theCalculatorobject.Calculatorclass: the invoker that receives and stores the concrete commands. It executes the commands by calling theirExecutemethods, and undoes them by calling theirUndomethods.Programclass: the client that creates aCalculatorobject and executes the concrete commands on it. It also invokes theUndomethod of theCalculatorobject to undo the last executed command.
Summary
- Use the command pattern to decouple the requester and receiver of a request by encapsulating the request as an object.