Command design pattern

Command design pattern
Command design pattern

Command design pattern encapsulates request as an object thereby allowing parameterize other objects with different request, log and queue requests, and support undoable operations.

Command Real World Example
Command Real World Example

Test client:

[TestFixture]
public class CommandPatternTest
{
    private Calculator _calculator;
    private Invoker<int> _invoker;

    [SetUp]
    public void SetUp()
    {
        //Arrange
        _calculator = new Calculator();
        _invoker = new Invoker<int>();
    }

    [TestCase(1, 2, 3)]
    public void InvokedAddCommand_OnValidParams_ReturnsExpectedResult(int a, int b, int expectedResult)
    {
        //Arrange
        var addCommand = new AddCommand<int>(_calculator, a, b);
        //Act
        var actualResult = _invoker.SetAndExecuteCommand(addCommand);
        //Assert
        Assert.AreEqual(expectedResult, actualResult);
    }

    [TestCase(3, 2, 1)]
    public void InvokedSubtractCommand_OnValidParams_ReturnsExpectedResult(int a, int b, int expectedResult)
    {
        //Arrange
        var subtractCommand = new SubtractCommand<int>(_calculator, a, b);
        //Act
        var actualResult = _invoker.SetAndExecuteCommand(subtractCommand);
        Assert.AreEqual(expectedResult, actualResult);
    }

    [TestCase(1, 2, 3)]
    public void InvokedUndo_ReturnsExpectedResult(int a, int b, int expectedResult)
    {
        //Arrange
        var addCommand = new AddCommand<int>(_calculator, 1, 2);
        _invoker.SetAndExecuteCommand(addCommand);
        _invoker.SetAndExecuteCommand(addCommand);
        //Act
        var actualResult = _invoker.Undo();
        //Assert
        Assert.AreEqual(expectedResult, actualResult);
    }
}

Invoker:

/// 
/// The Invoker holds a command and calls command's execute method
/// 
/// 
public class Invoker where  T : struct, IConvertible, IComparable
{
    private Stack<ICalculatorCommand> _commands = new Stack<ICalculatorCommand>();        

    public void SetCommand(ICalculatorCommand command)
    {
        _commands.Push(command);            
    }

    public T SetAndExecuteCommand(ICalculatorCommand command)
    {
        _commands.Push(command);
        return command.Calculate();
    }        

    public T Execute()
    {
        return _commands.Peek().Calculate();
    }

    public T Undo()
    {
        if (!_commands.Any())
            throw new InvalidOperationException();
            
        _commands.Pop();
        return _commands.Peek().Calculate();           
    }
}

Receiver:

public enum Operand
{
    Add = '+',
    Subtract = '-'
}

/// 
/// A commands receiver, performs real actions 
/// 
public class Calculator
{
    public dynamic PerformCalculation(Operand operand, dynamic arg1, dynamic arg2)
    {
        switch (operand)
        {
            case Operand.Add:                    
                return arg1 + arg2;
            case Operand.Subtract:
                return arg1 - arg2;
        }
        return null;
    }
}

Abstract command:

public interface ICalculatorCommand where T : struct, IConvertible, IComparable
{
    T Calculate();
}

Concrete commands

public class AddCommand : ICalculatorCommand where T : struct, IConvertible, IComparable
{
    private Calculator _calculator;
    private T _arg1, _arg2;

    public AddCommand(Calculator calculator, T arg1, T arg2)
    {
        _calculator = calculator;
        _arg1 = arg1;
        _arg2 = arg2;
    }

    public T Calculate()
    {
        return (T)_calculator.PerformCalculation(Operand.Add, _arg1, _arg2);
    }
}

public class SubtractCommand : ICalculatorCommand where T : struct, IConvertible, IComparable
{
    private Calculator _calculator;
    private T _arg1, _arg2;

    public SubtractCommand(Calculator calculator, T arg1, T arg2)
    {
        _calculator = calculator;
        _arg1 = arg1;
        _arg2 = arg2;
    }

    public T Calculate()
    {
        return (T)_calculator.PerformCalculation(Operand.Subtract, _arg1, _arg2);
    }
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s