Design Patterns: The Command Pattern

The Command Pattern is a popular pattern in object-oriented designs and it is commonly used in applications we interact with daily. The premise of this pattern revolves around bundling pieces of business logic into individual repeatable commands that can be executed when needed. The pattern promotes the single responsibility principle by breaking the logic into smaller commands responsible for performing a specific task. It also promotes the open/closed principle by allowing the design to be extended by adding new commands to the system without having to modify any code. This pattern also caters nicely to the use of IoC containers to honor the dependency inversion principle when creating new command instances. This shields the client from any dependencies within the system.

What is a Command?
A command is a single piece of business logic that is responsible for performing a specific task within the system. The command can be responsible for creating an instance, deleting an instance or perform any generic task against the system. Commands are also known in object oriented designs as ‘actions’ or ‘transactions’. In this pattern, commands typically implement a common interface that can be passed around within the system without having any knowledge of logic withing the command.

Advantages of Using the Command Pattern
Dividing the business logic into individual command provides several advantages that makes the design more robust and scalable. Command execution can be instantaneous or deferred. Multiple commands can be pushed into a queue or written to disc in order to be executed at a later time. Another advantage to using commands is the ability to perform validation on individual commands prior to executing them. The validation enforces the integrity of the command and prevents the execution if any required data is not provided. The command pattern facilitates the support of logging to a central log system to track all activities performed within the system. Finally, the command pattern allows to supporting the ‘Undo’ functionality. This is when one desires to revert back any changes performed by a specific command, also known as rolling back a transaction.

Required Supporting Patterns
Implementing the command pattern requires the knowledge and the use of other patterns in order to ensure a robust scalable design. The use of these patterns patterns might not be required, especially in simple applications. However, it is important to be at least familiar with these patterns when considering the command pattern. These patterns include the Strategy Pattern, the Null-Object Pattern, the Factory Pattern and the Singleton Pattern.

Example without Using the Command Pattern
The example below is a simple command line tool that allows users to create new Employees or delete existing ones. If the tool is invoked without any commands or parameters, the tool prints out the usage statement. Otherwise, the tool creates a new instance of ‘CommandExecutionService’ and it passes all the arguments to it.

Program.cs and Main

public class Program
{
   public static void Main(string[] args)
   {
      if (args.Length < 2)
      {
         PrintUsage();
         return;
      }

      var commandExecutionService = new CommandExecutionService();
      commandExecutionService.ExecuteCommand(args);
   }

   private static void PrintUsage()
   {
      Console.WriteLine("Usage: CommandPattern.exe Command Arguments");
      Console.WriteLine("Available Commands: ");
      Console.WriteLine("     AddEmployee");
      Console.WriteLine("     DeleteEmployee");
   }
}

CommandExecutionService.cs

public class CommandExecutionService
{
   public void ExecuteCommand(string[] arguments)
   {
      switch (arguments[0])
      {
         case "AddEmployee":
            AddEmployee(arguments[1]);
            break;
         case "DeleteEmployee":
            DeleteEmployee(arguments[1]);
            break;
      }
   }

   private void AddEmployee(string name)
   {
      Console.WriteLine("Adding Employee {0}", name);
   }

   private void DeleteEmployee(string name)
   {
      Console.WriteLine("Deleteing Employee {0}", name);
   }
}

Perhaps the most critical issue issue with the design above is in the CommandExecutionService. The service contains the ExecuteCommand function that contains a giant switch statement. The switch statement attempts to parse the first parameter to determine which function to invoke. The issue with this approach is that for each new command added to the system, the switch statement needs to be modified in order to support this new command. This clearly violates the open/close principle and limits the scalability of the design. In addition, the CommandExecutionService violates the single responsibility principle since it handles more than one responsibility. It is very likely that this service will grow quickly over time turning into a maintenance nightmare. Finally, performing tasks such as logging, validation and undo will be tightly coupled to the service and cannot be easily modified without a major refactoring.

Example Using the Command Pattern
The first step to implementing the command pattern is by defining a common command interface that all command implement. The interface will define all possible methods that can be executed on a command. Some of these supported methods maybe Execute(), Validate(), Undo(). To keep this example simple, the only method that is added to the interface is the Execute() method.

ICommand.cs

public interface ICommand
{
     void Execute();
}

The second important characteristic of the command pattern is to ensure that when a new command instance is constructed, it is supplied with all the context it needs for a successful execution. The client must be shielded from any knowledge of to construct a command. So all commands implementing the ICommand interface must have properties defined on them to supply the command with a proper context of execution. This is different from the Strategy pattern and other patterns where an object is passed in as a context to another object to perform various operations upon it. Below is an example of the AddEmployee command and the DeleteCommand:

AddEmployee.cs

public class AddEmployee : ICommand
{
     public string Name { get; set; }

     public AddEmployee(string name)
     {
          Name = name;
     }

     public void Execute()
     {
          Console.WriteLine("Adding Employee {0}", Name);
     }
}

DeleteEmployee.cs

public class DeleteEmployee : ICommand
{
     public string Name { get; set; }

     public DeleteEmployee(string name)
     {
          Name = name;
     }

     public void Execute()
     {
          Console.WriteLine("Deleting Employee {0}", Name);
     }
}

In order to ensure the robustness of the design, it is recommended that the command pattern implements another design pattern called the Null-Object pattern. This pattern will assist in the cases where the requested command in undefined or does not exist. Rather than returning a null and bloating the code with if-statements to guard against null references, a new NullCommand class is defined and returned in such scenarios. The Execute() method implementation within the NullCommand class performs no operations.

NullCommand.cs

public class NullCommand : ICommand
{
     public void Execute()
     {
     }
}

Defining the CommandFactory
A new CommandFactory class is introduced with the purpose of constructing all new commands. This factory is to replace the poorly designed CommandExecutionService from the previous design. There are many ways to design the factory and it is an involved topic. In this example, the design is kept as simple as possible with the introduction of a single factory that relies on refection to register all commands defined in the system. Other more involved factory patterns might introduce a common factory interface with a concrete implementation of a factory for each command. But that is beyond the scope of this example.

In the example below, the CommandFactory's construction scans the current assembly for all classes that implement the ICommand interface. They are then added to a list type available for instantiation. Next, a GetCommand() public method is added to the factory to parse the requested command by name and return a properly instantiated instance of the requested command that is initialized with it's proper context. In the case when an invalid command is requested, an instance of the NullCommand is returned.

CommandFactory.cs

public class CommandFactory
{
     private readonly List<Type> availableCommandTypes;

     public CommandFactory()
     {
          availableCommandTypes = new List<Type>();

          var iCommand = typeof (ICommand);
          var types = Assembly.GetExecutingAssembly().GetTypes();
         
          foreach (var type in types)
          {
               if (type.IsInterface || !iCommand.IsAssignableFrom(type))
               {
                    continue;
               }

               availableCommandTypes.Add(type);
          }
     }

     public ICommand GetCommand(string commandName, string argument)
     {
          var type = availableCommandTypes.SingleOrDefault(c => c.Name == commandName);

          if (type == null)
          {
               return new NullCommand();
          }

          return (ICommand) Activator.CreateInstance(type, argument ?? "");
     }
}

Finally, the Main method that serves as the client can be simplified. All it does is create an instance of the factory and invokes the GetCommand() method to get an instance of a command. It is worthy to mention that the CommandFactory can be turned into a singleton instance in order to amortize the cost of constructing a new factory over the life time of the application. The GetCommand() method will always return an valid instance of a command on which the Execute() method is invoked. The PrintUsage() method remains the same.

Program.cs and Main

public class Program
{
     static void Main(string[] args)
     {
          if (args.Length < 2)
          {
               PrintUsage();
               return;
          }

          var commandFactory = new CommandFactory();
          var command = commandFactory.GetCommand(args[0], args[1]);

          command.Execute();
     }

     private static void PrintUsage()
     {
          Console.WriteLine("Usage: CommandPattern.exe Command Arguments");
          Console.WriteLine("Available Commands: ");
          Console.WriteLine("     AddEmployee");
          Console.WriteLine("     DeleteEmployee");
     }
}

You may also like...

Leave a Reply

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