Ok, so, what started this whole "I gotta blog now!" thing was a short interview I did with Josh Holmes on how I implemented the command pattern to give myself a flexible Undo / Redo. What I did was simple, which probably also means, it could be better.
In short, in the Command Pattern you have three players. The Receiver (The guy who actually has real code to execute), The Invoker (The guy who normally calls the receiver to do something) and the Command (think of the command as a proxy... I’ll explain).
Ok, so say I’m writing a simple app that moves a robot every time the user clicks a specific button. The code would be something like this.
public class MyForm : Form
{
private void Button1_click(object sender, EventArgs e)
{
MoveRobotForward();
}
private void Button2_click(object sender, EventArgs e)
{
MoveRobotBackward();
}
public void MoveRobotForward()
{
//move him forward
}
public void MoveRobotBackward()
{
//Move him backward
}
}
So what happens when I want to undo this command, or for that matter, I want to undo a whole bunch of movements in sequence? I could store each movement in a collection right? Maybe even remove it from the collection, or something along those lines? That’s essentiatally what the command pattern is.
The basics of the pattern are to create an object out of a function call, then you store that object in a collection (typically in the invoker). Once it's stored in the collection, you can re-call it and execute it, or undo it, so on and so forth.
Let’s start with the Command class:
public abstract class Command
{
protected MyForm m_receiver;
public Command(MyForm in_receiver)
{
m_receiver = in_receiver;
}
public abstract void Execute();
public abstract void UnDo();
}
Ok, this is our “Base” command. Notice it has a refrence to our form, and requires a Execute and Undo method. They're abstract so no code yet.
Next up, our Invoker class. This is where the meat of the Undo/Redo lives.
public class Invoker
{
List<Command> commands;
static Invoker m_invoker;
static int m_location = 0;
private Invoker()
{
commands = new List<SequenceUCCommand>();
}
public static Invoker Instance()
{
if (m_invoker == null)
m_invoker = new Invoker();
return m_invoker;
}
public void ExecuteCommand(Command in_command)
{
if (in_command != null)
{
if (m_location < commands.Count)
{
int i = m_location;
while (i < commands.Count)
{
commands.RemoveAt(0);
}
}
commands.Add(in_command);
m_location++;
in_command.Execute();
}
}
public void Undo(int in_undoLevels)
{
for (int i = 0; i < in_undoLevels; i++)
{
if(m_location>0)
commands[--m_location].Undo();
}
}
public void Redo(int in_redoLevels)
{
for (int i = 0; i < in_redoLevels; i++)
{
if (m_location < commands.Count)
commands[m_location++].Execute();
}
}
}
Lots of code kind of. It’s real simple though. Let me explain before you walk away. The invoker will “Execute” a command on my behalf. Thanks to the wonders of Polymorphism, so long as the command inherits from “Command” were good. As we just saw, each command has to have Execute and Undo. This is where the ExecuteCommand, and Undo and Redo methods come in. Execute command will execute the method and add it to the collection. It’s also checking to see if there have been any commands undone before adding this command, and removes them. I’ll explain that one later. Undo/Redo, both increment/decrement the location in the collection we want to execute from (IE which command should I look at to execute).
Simple right?
Now here is the actual command, hopefully this will help make things make sense
public class MoveRobot:Command
{
private int m_forward;
public MoveRobot(MyForm in_reciver, bool in_forward):base(in_reciver)
{
m_direction = in_direction;
}
public override void Execute()
{
//Check direction
if (m_forward)
m_receiver.MoveRobotForward();
else
m_receiver.MoveRobotBackward();
}
public override void UnDo()
{
//check direction
if (m_forward)
m_receiver.MoveRobotBackward();
else
m_receiver.MoveRobotForward();
}
}
Ah Ha!! So… this command is responsible for actually calling the code on the form to “Do” something.
So we just modify our form to use the invoker..
public class MyForm : Form
{
private Invoker m_invoker = Invoker.Instance();
private void Button1_click(object sender, EventArgs e)
{
//MoveRobotForward(); instead of calling directly...
MoveRobot mr = new MoveRobot(this, true);
m_invoker.ExecuteCommand(mr);
}
private void Button2_click(object sender, EventArgs e)
{
MoveRobot mr = new MoveRobot(this, false);
m_invoker.ExecuteCommand(mr);
}
public void MoveRobotForward()
{
//move him forward
}
public void MoveRobotBackward()
{
//Move him backward
}
}
And Whalla! The robot moves AND I have a record of it!
So add these methods to the MyForm class and you’re done…
private void UnDo()
{
m_invoker.Undo(1);
}
private void ReDo()
{
m_invoker.Redo(1);
}
(Note the “Levels” argument. That means that we can “Undo” as many times as we have commands)
So In the end, you have more code, BUT you’ve created an implmentation of the common Command Pattern.
Plus think of the possiabilities. Say you need to “Record” a sequence of commands. You could create a whole bunch of commands as you execute them. Persist the Invoker collection to disk, and distribute it. When done that way it’s called a macro.
So. The user can’t quite figure out how to push the right buttons in the right order? Load that sequence up and hit play. Presto! Your done.
Drop me a line if you find this article horriable or great. Either way. Plus if you have any questions feel free to contact me.
Dave