Reversible Command Pattern
The Reversible Command design pattern encapsulates a request or an operation as an object, thereby allowing you to queue, log, and serialize requests to a store (volatile or durable) and create undo or rollback requests.
With this pattern you basically log all the requests and execute them through a command invoker entity.
Why is this important?
Most transactional resource managers are capable of logging user requests during transaction. When transaction is being committed, commands are executed on the resource. Because the commands had been captured during transaction, it is possible to examine those commands and create undo requests. Inside the rollback process, those undo commands can then be executed.
Where else can I use this?
In scenarios that you need to capture commands and replay those back. You have the ability to serialize the commands (they are simply .NET classes which can be serializable) and replay those at a later date.
Structure
At the heart of this pattern is an interface that defines a command. This interface exposes an Execute method which is called to execute the logged command. It also provides a ReverseCommand property which is a reference to an ICommand, capable of undoing the request.
As you can see, it is possible create a concrete command by implementing ICommand.
There is one more required entity here to queue all commands and to execute them. This entity is usually called CommandInvoker and is responsible for the following:
- Keep a list of all commands
- Execute all commands
- Keep track of all successfully executed commands
- Allow the user to revert back all successfully executed commands by executing their reverse commands
Sample Implementation
Here is a sample implementation of this pattern:
ICommand
public interface ICommand
{
void Execute();
ICommand ReverseCommand
{
get;
}
}
CommandInvoker
public class CommandInvoker
{
SynchronizedCollection<ICommand> _commands =
new SynchronizedCollection<ICommand>();
Queue<ICommand> _executedCommands;
/// <summary>
/// Call this method to add a new command to the
/// list of in-memory commands
/// </summary>
public void AddCommand(ICommand command)
{
// add commands and get them ready for execution
_commands.Add(command);
}
/// <summary>
/// Execute all the in-memory commands
/// </summary>
public void Execute()
{
// perform a double-check-lock
if (_commands.Count > 0)
{
lock (_commands.SyncRoot)
{
if (_commands.Count > 0)
{
_executedCommands = new Queue<ICommand>();
// now execute all the
// commands one at a time...
foreach (ICommand command in _commands)
{
try
{
command.Execute();
// keep track of all the
// successfully executed commands
ICommand rc = command.ReverseCommand;
if (rc != null)
{
_executedCommands.Enqueue(rc);
}
}
catch (Exception ex)
{
Debug.WriteLine(
string.Format(
"Exception = {0}\r\nCommand = {1}",
ex.Message, command.ToString()));
throw;
}
}
}
}
}
}
/// <summary>
/// Revert back all the successfully
/// executed commands
/// </summary>
public void RevertExecutions()
{
if (_executedCommands != null &&
_executedCommands.Count > 0)
{
lock (_commands.SyncRoot)
{
while (_executedCommands.Count > 0)
{
_executedCommands.Dequeue().Execute();
}
}
}
}
}
Concrete Commands
The next step is to build a few concrete commands. For this example I will create 2 simple commands (one reversing the other):
AddUser Command
public class AddUser : ICommand
{
private IUserManager _receiver;
public string Username { get; set; }
public AddUser(
IUserManager receiver, string username)
{
_receiver = receiver;
Username = username;
}
public void Execute()
{
_receiver.AddUser(Username);
}
public ICommand ReverseCommand
{
get
{
return new RemoveUser(_receiver, Username);
}
}
}
RemoveUser Command
This command will look identical to the AddCommand. Its ReverseCommand property will return an instance of the AddCommand (the reverse of Remove is Add).
As you can see both commands have access to a receiver. This is a reference to an object that the command will be executed on.
Finally,
Everything is in place; I just need to write some code to test it:
// create a user manager
UserManager userManager = new UserManager();
// create a command invoker
CommandInvoker invoker = new CommandInvoker();
// let's log a number of requests
invoker.AddCommand(
new AddUser(userManager, "Pedram"));
invoker.AddCommand(
new AddUser(userManager, "Sara"));
invoker.AddCommand(
new AddUser(userManager, "James"));
// display all users before the execution of all commands
Console.WriteLine("Before execution of commands");
Console.WriteLine(userManager);
// now lets execute all the commands
invoker.Execute();
// display all users after the execution of all commands
Console.WriteLine("After execution of commands");
Console.WriteLine(userManager);
// revert back the commands
invoker.RevertExecutions();
// display all users after the revert operation
Console.WriteLine("After revert execution of commands");
Console.WriteLine(userManager);
Console.ReadLine();
When the above code is executed, you’ll notice that although new commands are created, they are not actually executed until the Execute method on the Command Invoker is called. It is also possible to rollback the changes by calling RevertExecutions:
You can download the samples code from here (It is a Visual Studio 2008 beta 2 solution).
Comments
Anonymous
October 02, 2007
PingBack from http://www.artofbam.com/wordpress/?p=4613Anonymous
October 26, 2008
The comment has been removedAnonymous
May 20, 2009
Hi Pedram, I made a command pattern addition which I called command transaction (http://code-schmoof.com/2009/05/17/command-transactions/). With a transaction you can perform a set of commands, and if you need to cancel the set of commands (e.g. the user presses ESC), you can simply rollback. If you're interessted, check it out. Cheers, Nico