design pattern

Behavioral Design Patterns: Command

Not to be rude, but I hereby command you to check out today’s article in our ongoing Guide to Software Design Patterns series, in which we dive into the extremely useful command design pattern in all its glory! As another behavioral pattern, the command design pattern makes it easy to add work to a queue, which can be processed by a client at any moment in time. Furthermore, the client need not be aware of the underlying functionality or implementation of the queued work.

In this article we’ll closely examine the command design pattern, exploring a real world example of the pattern in action, along with a fully-functional C# code sample intended to illustrate how the pattern can be used in your own coding endeavors. Let’s get this party started!

In the Real World

To explain a real world example of the command pattern, we should first outline the basic components that make up the bulk of its logic.

  • Receiver – Receives actions via Commands.
  • Command – Binds an action to a Receiver.
  • Invoker – Handles a collection of Commands and determines when Commands are executed.
  • Client – Manages interactions between Receiver / Command and Command / Invoker.

While that’s all very high level and abstract, it may begin to make sense when you consider that the postal service as a modern, real world example of the command pattern in action. A recipient waiting to get some mail is a receiver. Letters would be considered commands, each awaiting their time to be “executed,” by being delivered to the appropriate recipient. A mailman/mailwoman is obviously the invoker, handling the collection of letters (commands) and determining when they are delivered (executed). The postoffice itself is, therefore, the client, as it determines which letters (commands) are assigned to which mailpersons (invokers).

Moreover, the command pattern is intended to make it easy to revert or undo a previous command action, if necessary. As we all know, this also applies to postal deliveries, since a refused letter or package can easily be picked up and returned to sender, thereby reverting the initial command action.

How It Works In Code

As with other behavioral pattern code samples, the command pattern example is somewhat complex at first glance. We’ll completely break down our sample below, so you can easily understand everything that’s going on in the code. As an avid gamer myself, I decided to implement a basic Character modification system for our command design pattern example. Here’s the basic rundown of the logic:

  • Character class – Our receiver, which contains a few standard statistics (Agility, Charisma, and Strength) that we want to manipulate.
  • Modification class – Our command class, which allows us to modify a statistic for the associated Character instance.
  • ModificationManager class – Our invoker class, which holds the Modifications queue and determines when queued commands should be invoked.
  • Program.Main(string[]) method – Our simple client class, which creates two Characters, a number of Modifications, then proceeds with executing and reverting them to see how Character statistics change.

That’s the big picture of what we’re going for here, so now we’ll start with the full code sample for easy copy-pasting. Afterward, we’ll start digging into it to see exactly what’s going on:

The Command.Statistics namespace contains just a few statistic classes (Strength, Agility, and Charisma), each of which merely contain a Value property. They’re not a fundamental part of the command pattern, so we won’t go into anymore detail on those. Instead, let’s take a look at the Character class:

This is our core Receiver class, which is basically the object we’ll be acting upon via Modifications. As you can see, each Character just has a Name and a set of statistic properties, each of which have Value properties of their own that default to 0.

Next, we move onto the Modification class. However, before we do so, let’s look at the IModification interface that Modification implements:

One of the goals of our command pattern example is the ability to revert modifications that have already been made. This effectively allows the system to freely perform any modifications, in any order, and even to roll them back in any order as well, without confusing the logic or incorrectly modifying the underlying Character statistic values. To that end, we’ll be using the Command.Status enumeration values throughout our logic to assign and check the current stage of each Modification.

The IModification interface is really the actual Command object we’ll be using. However, since it’s an interface, we refer to classes that implement said interface as ConcreteCommands. The fundamental methods we need are Execute() to trigger an action and Revert() to roll an action back. We also store the Status and unique Id, in case we need to refer to a Modification elsewhere in code.

Now we get to the Modification class, which is ultimately a Command, although we could have multiple Command classes to implement the IModification interface, so calling Modification a ConcreteCommand is a bit more accurate in this context.

The Modification constructor method accepts a Character, a StatisticType (which is just an easy way to refer to Agility, Charisma, or Strength statistics), and a decimal value. The value parameter represents the potential change in value for the passed StatisticType associated with the passed Character instance. Fundamentally, that’s all that a Modification does — it changes one of the statistic property values of the passed Character.

This value change is performed in the Execute() method, which passes most of its logical behavior to the UpdateValue(bool) method. This method uses the CharacterStatisticValueProperty and CharacterStatistic properties — both of which use a bit of reflection magic to obtain their underlying represented object values — to calculate and update that new underlying Value. The bool isReversion parameter allows us to use UpdateValue(bool) for both Execute() and Revert() calls, with almost no logical difference between the two methods.

Next let’s look at the ModificationManager, which is our invoker class. It handles the Modification queue and determines when Modifications need to be executed, reverted, or ignored:

The most important component of the ModificationManager is the List<IModification> _queue property. This collection is used by every method within the class. For example, the HasQueue property uses LINQ to check if any queued objects have either a failing Status or are Queued (indicating they haven’t been processed yet).

The AddModification(IModification) method allows us to add Modifications to the queue. Similarly, RevertModification(IModification) attempts to find the passed Modification in the queue. If it’s found and has recently been successfully executed, the Revert() method is called on that Modification, which rolls back the value of the Character statistic it’s associated with. The RevertModification(Guid) overload allows us to perform reversion based on Id instead of an actual Modification instance argument.

Lastly, the ProcessQueue() method will be used to perform the majority of logical processing of the queue. This can be safely executed at any time, and simply finds any queued objects that recently failed or have never been executed. This is why a distinction between the Status.RevertFailed and Status.ExecuteFailed enumeration values is important, since we can use the Status value to determine which action to take on the failed Modification.

Lastly, we get to actually using our command pattern in some way, which is where the Program.Main(string[]) method comes in. This is effectively our client object:

Nothing too crazy going on here. We start by creating a ModificationManager instance, along with a couple Characters, and a number of Modification instances for assorted statistics and values. Then, we add the Modifications, in an irrelevant order, to the manager queue, before calling manager.ProcessQueue() to apply all the modifications. The output, up to this point, shows everything working as expected:

Now that the queue contains some Modifications we can revert any of these changes we need to, at any time. In this case, our code does so right away, but our assertions verify that the appropriate statistic Values are reverting as expected. We also confirm that we can call RevertModification(Guid) using an Id rather than a Modification instance still results in proper behavior. Sure enough, the output (and passing tests) confirm everything is working:

So there you have it! A small but relatively robust example of the command design pattern in action. For more information on all the other popular design patterns, head on over to our ongoing design pattern series here!