[C# Design Patterns] The Command Pattern

This article will demonstrate the “Command” design pattern.

This design pattern is a little complex. It uses several different parts. However, it’s useful to know when you build enterprise-level programs, which need to be reliable and scalable.

 

View the video version here:

 

What it is

Normally, when you want to do something to an object, you call a function on that object. The function starts to execute as soon as you call it.

Here’s a typical, bank account class, where the Deposit and Withdraw functions are immediately executed when they’re called.

 

Sometimes, you don’t want to do execute your functions immediately.

You can use the Command pattern to add work to a queue, to be done later. You can use it to retry, if a command cannot execute properly. You might be able to use this to add “undo” capabilities to a program.

 

Parts of the Command Design Pattern

There are four parts to the Command pattern.

Command: Classes that execute an action (perform a function).

Receiver: Business objects that “receive” the action from the Command.

Invoker: This class tells the Commands to execute their actions. The Invoker can sometimes be a queue (when it holds commands to be executed later), a pool (when it holds commands that can be executed by different programs/computers), or let you do more things with your commands (retry failed commands, undo commands that were executed, etc.)

Client: The main program that uses the other parts.

 

Examples

For this example, we’ll write a program for a bank.

We want to deposit money, withdraw money, and do transfers between accounts.

 

Account.cs

This is the Receiver. Our commands will execute something on the Account objects. For example, a Deposit command will increase the Account object’s Balance.

 

 

ITransaction.cs

This interface defines what must exist in our Command objects.

The Execute method is what will be called by the Invoker. IsCompleted will be used to let us know is the Command executed successfully – so we can remove the completed Commands from the Invoker’s queue.

I’m using an interface here; however, you could do the same thing with an abstract base class.

 

These are the Command objects, which implement the ITransaction interface.

If you aren’t familiar with an interface, it’s just a way to say, “Every class that implements this interface, must have these properties/methods/etc.” You could also use

Notice that the constructors are different – the Transfer Command takes two accounts. That’s OK. The thing we care about is that every Command has an Execute function, and it has everything it needs to perform the business function.

 

Deposit.cs

 

Withdraw.cs

 

Transfer.cs

 

NOTE: The Execute methods in Withdraw and Transfer will not run if there is not enough money in the account. So, IsCompleted will still be False, and the Command object will still be a pending transaction in the TransactionManager.

 

TransactionManager.cs

TransactionManager is the Invoker. It holds the Command objects, and tries to perform the Execute on each one (when the Client asks to process all the Command objects).

 

The Client creates Command objects and sends them to the Invoker – in this sample, through the AddTransaction function. The Commands will be held in the _transactions list, until the Client calls ProcessPendingTransacations. Then, the Invoker will try to Execute each Command that has not already been completed.

Notice that the Invoker doesn’t know anything about what the Command objects do, or what parameters they need. All it needs to know is that the Command can be executed.

We could create additional Command classes: OpenAccount, CloseAccount, PayInterest, etc. And, if they implement the ITransaction interface, the Invoker can process them. This makes it easier to add more capabilities to the program – and they would automatically be able to use the apply/retry/undo/etc. features that are in the Invoker.

 

TestTransactionManager.cs

For this example, TestTransactionManager (a unit test class) is the simulated Client.

 

 

The three test methods simulate what our client program might do, and verify that the Invoker executes the Commands – or not, if the Command should not complete.

 

Enhancement, with the ability to “Undo” a Command

I was asked how to implement the undo, and manage the Invoker (run it on a schedule and delete old Commands).

The short answer to adding the undo ability is to add an undo to the ITransaction interface, like this:

Then, add an Undo function to each Command class. The Undo code for Deposit would look like the Execute code for Withdraw, and vice versa. The Undo for Transfer would reverse the accounts.

However, this feature request leads to more questions.

 

Determining all the requirements

Will we want to undo all Commands, or only selected Commands? If it’s only selected Commands, how will we know which ones to undo? We’ll probably need a unique ID for each Command object, which means we need to define an ID property in ITransaction.

We’ll also want to only undo Commands that were successfully completed. If we undo a Command, does that mean we can run Execute on it again? Or, should we only be able to delete it? Do we need another Boolean property, to show if the Undo was completed? Maybe we need a CommandStatus enumerator, with values for all possible states of the Command object.

What criteria should we use to delete a Command from the TransactionManager object? If we want to call the Undo function on some Commands, we can’t delete the Command objects after the Execute is successful.

By the way, figuring out questions like these is a large part of how I spend my day programming. It’s a lot more thinking, questioning and planning, than typing.

 

Answers for the requirement questions

For this example, we’ll say:

  • We want to Undo a Command based on an ID.
  • You can only Undo a Command if the Execute was completed.
  • If the Undo fails, set the Command’s status to UndoFailed – so we can retry the Undo again.
  • If the Undo succeeds, set the Command’s status to UndoSucceeded – so we can delete it. We do not want to allow the user to call Execute again, after an Undo.
  • We will delete all completed Commands over 15 days old. So, we also need a date property in ITransaction.

 

Updated code, with undo features

Here is the new code, with Undo capabilities added. I wrote this quickly, so please tell me if you see any errors.

 

Enums.cs

This is for all the different possible states of a Command object.

 

ITransaction.cs

We have the new properties, and the Undo function, declared in the interface now. The Status property replaces the old Boolean IsCompleted property.

 

Deposit.cs

 

Withdraw.cs

 

Transfer.cs

 

The Command class constructors accept a parameter for the ID property, set the CreatedOn property to the DateTime the object was instantiated (using UTC time, which is a good idea for any DateTime that might be stored somewhere – like a database or message queue), and set the initial Status to Unprocessed.

The Execute and Undo functions set the Status for all possible results, so we know if the Execute/Undo succeeded or failed.

 

TransactionManager.cs

Now that we have more possible states for the Command objects, the functions need to make sure they only use Commands that are in the appropriate state.

HasPendingTransaction property has been changed to look for Command objects that still need to be processed – because they are new, or an Execute or Undo failed.

ProcessPendingTransdactions has been changed to call Execute, for transactions that are unprocessed or where Execute failed. Then, it calls Undo on transactions where a previous call to Undo failed.

UndoTransactionNumber will call Undo on the appropriate Command object, if it is still in the _transactions list, and it has been successfully executed. If the Undo succeeds, the Command object is removed from the transactions list.

RemoveOldTransactions is a new method to delete the Command objects that successfully execute, and are more than 15 days old.

NOTE: We could have moved throwing an exception if you try to undo a Command object where the status is not ExecuteSucceeded into the Undo functions.

 

TestCommandPattern.cs

I expanded the unit tests to also check the Undo function.

 

Managing the Invoker (TransactionManager)

Normally, I use this pattern with a program that runs on a schedule – once a night, or every few hours.

To do that, I create a Windows Service solution. The service is installed on a server in a network data center – so it should always be running.

The service would have a Timer object, that knows the time of day, or interval, to call TransactionManager.ProcessPendingTransactions.

I would also put the retry rules into there – if needed. For example, if HasPendingTransactions is true, after running ProcessPendingTransaction, wait 15 minutes and re-run ProcessPendingTransactions.

You could also add a Timer to call the RemoveOldTransactions once a night. Or, however often you wanted it to run.

 

Where I’ve found the Command Design Pattern useful

I often use the Command Design Pattern with a message queue application. We’ll use the message queue for logging, and to save our commands to disk. If we re-boot the computer (or if it crashes), our program can read the incomplete commands from the message queue, and continue working – without losing any data.

I’ve normally used this with programs that submit data to web services. If the web service isn’t working, we still have the command object in the queue. When the web service is working again, the queue will try to Execute all the unsent Command objects.

You normally don’t need to use the Command design pattern for personal projects. It makes the program unnecessarily complex. However, it can be useful for a large business application that needs to be extremely reliable.

All my design pattern lessons

Source code for my design pattern lessons

8 thoughts on “[C# Design Patterns] The Command Pattern

  1. Thanks for this! It was certainly helpful! What about when the command is an asynchronous operation that returns some result that needs to then be parsed in some intelligent way. Would the receiver then be the handler for that command?

    1. You’re welcome!

      There are a few different ways you could do this, depending on exactly what you need the program to do.

      My first thought is to make the Invoker function asynchronous, by adding this to the Invoker (TransactionManager) class, for example:
      using System.Threading.Tasks;

      public Task ExecuteTransactionAndReturnSuccess(ITransaction transaction)
      {
      return Task.Run(() =>
      {
      _transactions.Add(transaction);

      transaction.Execute();

      return transaction.IsCompleted;
      });
      }

      Implementing that, in the calling code (in this case, the TestCommandPattern class), would look like:
      [TestMethod]
      public void Test_Async()
      {
      TransactionManager transactionManager = new TransactionManager();
      Account checking = new Account("Mike Brown", 1000);
      bool result = transactionManager.ExecuteTransactionAndReturnSuccess(new Withdraw(checking, 750)).Result;

      Assert.IsTrue(result);
      Assert.AreEqual(250, checking.Balance);
      }

      If the asynchronous code is part of the Command class’ Execute function, and you want to use an eventhandler for when the async code is complete (I’m guessing that’s what you mean, when asking if the receiver will be the handler for that command), I think the handler would probably need to be in the Invoker. When the Invoker function receives a Command, it set the eventhandler on the Command object, executes the Command code, then waits for the event to be raised by the Command object.

      If I misunderstood that, or if you still have questions, please let me know. If you have a code sample you could post someplace (like https://gist.github.com/), I might be able to give a more exact answer.

  2. Wow, Sir, your examples are the best. It would be great if you could provide such a great examples for other design patterns.

    Well done!

Leave a Reply

Your email address will not be published. Required fields are marked *