The command pattern is another one of those patterns that we all (whether we realize it or not) see almost every day. It’s commonly used in UI development but it’s a pattern that can be applied in many situations. In rails, ActiveRecord migrations are a perfect example of a command implementation, including “up” and “down” methods for applying and rolling back a migration respectively.

The beauty of the command pattern is it’s ability to encapsulate complicated logic into an easy to use interface. This usually includes:

  1. An ICommand interface or base class that defines the command structure
  2. Concrete command implementations
  3. A receiver that is fed commands to execute. When these commands are actually executed is completely independent of when the receiver is given the commands. It may choose to execute them right away, but it may just as well decide to hold on to them for later use.

Source Code

All of the code in this example is available to download:

Download the Source

Or, you can pull the source directly from github using the below clone url:

git://github.com/johnmiller/CommandPattern.git

Example Overview

The example below involves a robot that moves around on a XY plane similar to:

XYPlane

The robot is given starting coordinates and moves from there. However, it can only perform certain actions:

  1. Move forward
  2. Turn left
  3. Turn right
  4. Reset

As it is given commands, it stores them in an internal collection. If reset is called, it “undo’s” all the commands executed to that point.

C# Example

The ICommand is pretty simple and only includes two method declarations: Execute() and Undo(). All of our concrete commands will need to implement this interface.

public interface ICommand
{
void Execute();
void Undo();
}

Before we create any concrete command classes, let’s take a look at our Robot class. When a new robot is created, we give it a starting location and direction. Notice that when a user calls ExecuteCommand() it immediately performs the instructions given and then stores the command in an internal list. If Reset() is called it iterates through the list in reverse order and calls Undo() on each.

public class Robot
{
public Location Position { get; set; }
public string Direction { get; set; }
private IList<ICommand> _commandsExecuted = new List<ICommand>();

public Robot(Location position, string direction)
{
Position = position;
Direction = direction;
}

public void ExecuteInstruction(ICommand command)
{
command.Execute();
_commandsExecuted.Add(command);
}

public void Reset()
{
foreach (var command in _commandsExecuted.Reverse())
command.Undo();
}
}

The Robot.Position property is of type Location which is a simple class holding the X and Y coordinates.

public class Location
{
public int X { get; set; }
public int Y { get; set; }

public Location(int x, int y)
{
X = x;
Y = y;
}
}

The first command we’ll look at is the MoveForwardCommand which…moves the robot forward one space. But we need to be sure that the robot is moving forward in the direction that it is facing. Internally it contains a list of algorithms for moving the robot depending on the based on it’s current direction. It can also go backwards if Undo() is called.

public class MoveForwardCommand : ICommand
{
private Robot _robot;
private IDictionary<string, Action<Robot>> _moveActions;

public MoveForwardCommand(Robot robot)
{
_robot = robot;
_moveActions = new Dictionary<string, Action<Robot>>
{
{"N", r => r.Position.Y += 1 },
{"E", r => r.Position.X += 1 },
{"S", r => r.Position.Y -= 1 },
{"W", r => r.Position.X -= 1 }
};
}

public void Execute()
{
_moveActions[_robot.Direction](_robot);
}

public void Undo()
{
var oppositeDirection = Compass.GetOppositeDirection(_robot.Direction);
_moveActions[oppositeDirection](_robot);
}
}

The Undo() method uses a class called Compass to find the backwards direction. This is a simple utility class to aid in finding left, right, and opposite directions. To find the direction to the right or left, we find the current direction’s place in the list and move accordingly. Although we need to ensure that when asked to turn right when facing west, it moves to the start of the list to get north (the opposite holds true when turning left when facing north).

public class Compass
{
private static IList<string> _directions = new List<string>{"N", "E", "S", "W"};

public static string GetRightTurnDirection(string direction)
{
if (direction == _directions[3]) return _directions[0];

return _directions[_directions.IndexOf(direction) + 1];
}

public static string GetLeftTurnDirection(string direction)
{
if (direction == _directions[0]) return _directions[3];

return _directions[_directions.IndexOf(direction) - 1];
}

public static string GetOppositeDirection(string direction)
{
var directionToTheRight = GetRightTurnDirection(direction);
return GetRightTurnDirection(directionToTheRight);
}
}

The next set of commands we’ll create will allow the robot to turn left and right. This doesn’t change the space the robot is on, only the direction that it is looking.

public class TurnLeftCommand : ICommand
{
private Robot _robot;

public TurnLeftCommand(Robot robot)
{
_robot = robot;
}

public void Execute()
{
_robot.Direction = Compass.GetLeftTurnDirection(_robot.Direction);
}

public void Undo()
{
_robot.Direction = Compass.GetRightTurnDirection(_robot.Direction);
}
}

public class TurnRightCommand : ICommand
{
private Robot _robot;

public TurnRightCommand(Robot robot)
{
_robot = robot;
}

public void Execute()
{
_robot.Direction = Compass.GetRightTurnDirection(_robot.Direction);
}

public void Undo()
{
_robot.Direction = Compass.GetLeftTurnDirection(_robot.Direction);
}
}

Last, let’s take a look at a unit test that puts the robot to work (the source code linked to earlier in the post includes a lot more unit tests if you’re interested in seeing more!).

[TestFixture]
public class When_a_robot_is_given_multiple_commands
{
private Robot _robot;

[SetUp]
public void EstablishContext()
{
_robot = new Robot(new Location(5, 5), "W");
_robot.ExecuteInstruction(new TurnLeftCommand(_robot));
_robot.ExecuteInstruction(new TurnLeftCommand(_robot));
_robot.ExecuteInstruction(new MoveForwardCommand(_robot));
_robot.ExecuteInstruction(new TurnRightCommand(_robot));
_robot.ExecuteInstruction(new MoveForwardCommand(_robot));
_robot.ExecuteInstruction(new TurnRightCommand(_robot));
_robot.ExecuteInstruction(new MoveForwardCommand(_robot));
_robot.ExecuteInstruction(new MoveForwardCommand(_robot));
_robot.ExecuteInstruction(new TurnRightCommand(_robot));
}

[Test]
public void Should_end_up_at_the_expected_location()
{
Assert.That(_robot.Position.X, Is.EqualTo(4));
Assert.That(_robot.Position.Y, Is.EqualTo(4)); ;
}

[Test]
public void Should_be_facing_the_expected_direction()
{
Assert.That(_robot.Direction, Is.EqualTo("N"));
}

[Test]
public void Should_be_able_to_undo_the_command()
{
_robot.Reset();
Assert.That(_robot.Position.X, Is.EqualTo(5));
Assert.That(_robot.Position.Y, Is.EqualTo(5));
Assert.That(_robot.Direction, Is.EqualTo("W"));
}
}

Ruby Example

With ruby we won’t need to create an abstraction for our command objects, so we’ll jump right into creating our Robot object. Besides basic syntax differences, this is very similar to our C# implementation.

class Robot
attr_accessor :position, :direction

def initialize(position, direction)
@position = position
@direction = direction
@commands_executed = []
end

def execute_instruction(command)
command.execute
@commands_executed << command
end

def reset
@commands_executed.reverse_each{|x| x.undo}
end
end
&#91;/source&#93;

It also uses a custom Location class to store X and Y coordinates.

&#91;source language='ruby'&#93;
class Location
attr_accessor :x, :y

def initialize(x, y)
@x = x
@y = y
end
end
&#91;/source&#93;

Next up is the MoveForwardCommand. The @move_action variable contains a hash of Proc objects which contain the logic for moving the robot. Note that we use the “call” method to run the proc.

&#91;source language='ruby'&#93;
class MoveForwardCommand
def initialize(robot)
@robot = robot
@move_actions = {:N => Proc.new{|r| r.position.y += 1},
:E => Proc.new{|r| r.position.x += 1},
:S => Proc.new{|r| r.position.y -= 1},
:W => Proc.new{|r| r.position.x -= 1}}
end

def execute
@move_actions[@robot.direction].call @robot
end

def undo
opposite_direction = Compass.get_opposite_direction @robot.direction
@move_actions[opposite_direction].call @robot
end
end

Like our C# example, we make use of a custom Compass class to help find our directions. I really like the explicit “first” and “last” properties that come with the array object, seems to make the code a little easier to read.

class Compass
@directions = [:N, :E, :S, :W]

def self.get_right_turn_direction(direction)
return @directions.first if direction == @directions.last
@directions[@directions.index(direction) + 1]
end

def self.get_left_turn_direction(direction)
return @directions.last if direction == @directions.first
@directions[@directions.index(direction) - 1]
end

def self.get_opposite_direction(direction)
direction_to_the_right = get_right_turn_direction direction
get_right_turn_direction direction_to_the_right
end
end

And our commands to allow the robot to turn left and right.

class TurnLeftCommand
def initialize(robot)
@robot = robot
end

def execute
@robot.direction = Compass.get_left_turn_direction @robot.direction
end

def undo
@robot.direction = Compass.get_right_turn_direction @robot.direction
end
end

class TurnRightCommand
def initialize(robot)
@robot = robot
end

def execute
@robot.direction = Compass.get_right_turn_direction @robot.direction
end

def undo
@robot.direction = Compass.get_left_turn_direction @robot.direction
end
end

Last we’ll take a look at a unit test to that puts the robot through it’s paces. (There are also many more tests in the source code download if you’d like see more examples).

describe "When asked to perform multiple commands" do
before(:each) do
@robot = Robot.new Location.new(5,5), :W
@robot.execute_instruction TurnLeftCommand.new(@robot)
@robot.execute_instruction TurnLeftCommand.new(@robot)
@robot.execute_instruction MoveForwardCommand.new(@robot)
@robot.execute_instruction TurnRightCommand.new(@robot)
@robot.execute_instruction MoveForwardCommand.new(@robot)
@robot.execute_instruction TurnRightCommand.new(@robot)
@robot.execute_instruction MoveForwardCommand.new(@robot)
@robot.execute_instruction MoveForwardCommand.new(@robot)
@robot.execute_instruction TurnRightCommand.new(@robot)
end

it "should end up at the expected location" do
@robot.position.x.should == 4
@robot.position.y.should == 4
end

it "should be facing the expected direction" do
@robot.direction.should == :N
end

it "should be able to undo all of the commands" do
@robot.reset
@robot.position.x.should == 5
@robot.position.y.should == 5
@robot.direction.should == :W
end
end

In our next post we’ll take a look at how we can solve the same problem using the State pattern!

kick it on DotNetKicks.com