Design patterns: Command

The Command design pattern encapsulates a request as an object. This will allow adding additional functionality to the request. A typical example is the undo/redo functionality.

The command object will be used as mediator between the client and the receiver and ads its additional behavior. For example we could have a calculator application. The client will do some calculation by requesting the according functions of the receiver, in this case a calculation object. If we want to implement undo/redo functions, we could now add an according command object. The client will call the command instead of the calculator methods. The command itself will call the calculator methods. And it offers additional behaviors like the undo/redo functionality.

The following example will show a possible implementation of this pattern. At first we implement the client and the receiver for the calculator example. So let’s start by defining the interface for the calculator.

enum Operation
{
    Summation,
    Subtraction,
    Multiplication,
    Division
}

interface ICalculator
{
    int ExecuteOperation(Operation operation, int operand);
}

At next we implement the calculator.

class Calculator : ICalculator
{
    private int _currentValue = 0;

    public int ExecuteOperation(Operation operation, int operand)
    {
        switch (operation)
        {
            case Operation.Summation: _currentValue = _currentValue + operand; break;
            case Operation.Subtraction: _currentValue = _currentValue - operand; break;
            case Operation.Multiplication: _currentValue = _currentValue * operand; break;
            case Operation.Division: _currentValue = _currentValue / operand; break;
            default: throw new ArgumentException();
        }

        return _currentValue;
    }
}

And we use the implemented calculator within a console application.

static void Main(string[] args)
{
    ICalculator calculator = new Calculator();

    Console.WriteLine("+ 2 = " + calculator.ExecuteOperation(Operation.Summation, 2));
    Console.WriteLine("* 5 = " + calculator.ExecuteOperation(Operation.Multiplication, 5));
    Console.WriteLine("- 3 = " + calculator.ExecuteOperation(Operation.Subtraction, 3));

    Console.ReadKey();
}

Now we want to add undo/redo features. This can be done by using the command design pattern. So we want to add a calculator command which offers a undo function. We don’t need an explicit redo function as a redo is equal to a repeated execution of the command.

interface ICalculatorCommand
{
    int ExecuteOperation();
    int UndoOperation();
}

The command itself is implemented by using the calculator object. It will redirect the client calls to the calculator and adds the undo function.

class CalculatorCommand : ICalculatorCommand
{
    private ICalculator _calculator;
    private Operation _operation;
    private int _operand;

    public CalculatorCommand(
        ICalculator calculator,
        Operation operation,
        int operand)
    {
        _calculator = calculator;
        _operation = operation;
        _operand = operand;
    }

    public int ExecuteOperation()
    {
        return _calculator.ExecuteOperation(_operation, _operand);
    }

    public int UndoOperation()
    {
        Operation undoOperation = GetUndoOperation();

        return _calculator.ExecuteOperation(undoOperation, _operand);
    }

    private Operation GetUndoOperation()
    {
        switch (_operation)
        {
            case Operation.Summation: return Operation.Subtraction;
            case Operation.Subtraction: return Operation.Summation;
            case Operation.Multiplication: return Operation.Division;
            case Operation.Division: return Operation.Multiplication;
            default: throw new ArgumentException();
        }            
    }
}

The client, in our example the console application, will now have the possibility to store the calculation command. This will allow adding unlimited undo and redoing steps. In this example we undo and redo the last two calculations.

static void Main(string[] args)
{
    ICalculator calculator = new Calculator();
    ICalculatorCommand command;
    List<ICalculatorCommand> commands = new List<ICalculatorCommand>();

    command = new CalculatorCommand(calculator, Operation.Summation, 2);
    Console.WriteLine("+ 2 = " + command.ExecuteOperation());
    commands.Add(command);

    command = new CalculatorCommand(calculator, Operation.Multiplication, 5);
    Console.WriteLine("* 5 = " + command.ExecuteOperation());
    commands.Add(command);

    command = new CalculatorCommand(calculator, Operation.Subtraction, 3);
    Console.WriteLine("- 3 = " + command.ExecuteOperation());
    commands.Add(command);

    //undo last two command
    Console.WriteLine("undo = " + commands[2].UndoOperation());
    Console.WriteLine("undo = " + commands[1].UndoOperation());

    //undo last two command
    Console.WriteLine("redo = " + commands[1].ExecuteOperation());
    Console.WriteLine("redo = " + commands[2].ExecuteOperation());

    Console.ReadKey();
}
Werbung
Dieser Beitrag wurde unter .NET, C#, Design Pattern veröffentlicht. Setze ein Lesezeichen auf den Permalink.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit deinem WordPress.com-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s