Using the Command Object Design Pattern in C# ASP .NET

modified

Introduction

The Command Object design pattern can be a powerful tool to use in development of C# ASP .NET web applications. The Command design pattern brings object oriented design to an otherwise non-object oriented piece of data - a command.

It is common to create classes and functions which operate by accepting a command to perform. The command may be a simple string of text or a globally defined integer. In both cases, this command is typically acted upon and then discarded. By wrapping a basic command within a Command Object design pattern, .NET applications can take advantage of the power to perform additional processing on the Command object, log commands, add additional parameters, and much more.

Command Design Patterns in C# ASP .NET Web Applications

What’s Wrong with an Integer Command?

The basic method for issuing a command to a class or function is by assigning a global unique integer to be a dedicated command message. The function performs a switch on the number to determine what processing to execute.

Basic Command Design Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const int COMMAND_PRINT 1001
const int COMMAND_SPEAK 1010
void ExecuteCommand(int iCommand)
{
   if (iCommannd == COMMAND_PRINT)
   {
      ...
   }
   else if (iCommand == COMMAND_SPEAK)
   {
      ...
   }
   else if (...)
   {
      ...
   }
}

While the above code performs the job, enhanced functionality can be obtained by wrapping the integers within a Command design pattern. The command can then be further enhanced with parameters to indicate speciailized properties, logging to record commands that were executed, or even undo a previous command. A generalized example of using the Command design pattern is shown below.

Command Design Pattern Example

1
2
3
4
5
6
MyPrinter = new MyPrinter(); // the receiver to act upon the command
MyInvoker = new MyInvoker(); // the initiator of the command
Command MyCommand = new ConcreteCommand(MyPrinter, COMMAND_PRINT);
MyInvoker.Execute(MyCommand);

It All Starts with the Command

To implement the Command design pattern, you begin by defining a generic Command object. This object will hold the command ID and contain the neccessary functions to invoke the command, as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
abstract class Command
{
    protected IReceiver receiver;
    protected int iID;
    // Give each command a unique identifier.
    private static int gID = 0;
    public Command(IReceiver aReceiver)
    {
        receiver = aReceiver;
        // Increment the unique identifier.
        gID++;
        // Give this command instance its id.
        iID = gID;
    }
    public int GetID()
    {
        return iID;
    }
    public abstract void Execute();
    public abstract string GetCommandText();
}

Note in the above definition, we use an interface for the Receiver object. The receiver is the one who performs an action depending on the command it receives. The command object will be linked to a receiver, via the interface.

We also include a unique ID to refer to the command later on. Each command has one automatically generated by the static integer. While this is optional, it helps enhance the command object.

The Execute() function is used to invoke the command object, which tells the Receiver to perform its action. The command is defined as an abstract class (a class which can not be instantiated, only inherited from) rather than an Interface, because we define a few function bodies within the class.

Since the Command object is linked to the Receiver, it seems only natural that we design the Receiver object next.

Work, Work!

To keep the Command design pattern as generic as possible, interfaces should generally be used in place of concrete class definitions. This promotes reusability in your C# .NET code. Therefore, we begin by defining the Receiver interface.

1
2
3
4
interface IReceiver
{
   void Action(string strText);
}

This interface is extremely basic. Each Receiver will contain an Action() function, which is performed when a command is received. Next, we define a concrete implementation of the Receiver. Note, since we use an interface to define the Receiver to the Command object, you can create many different types of Receivers and they can all be used within your Command design pattern.

1
2
3
4
5
6
7
8
9
10
11
class HelloWorldPrinter : IReceiver
{
    #region IReceiver Members
    public void Action(string strText)
    {
        Console.WriteLine(strText);
    }
    #endregion
}

This receiver will function as a “printer”. Any command he receives is simply printed to the console. With the Receiver now defined, an Invoker must be defined.

The Invoker Starts the Show

The Invoker is the object which initially executes a command. He is provided with a Command object and executes it (which then causes the Command to invoke the Receiver’s action function). The Invoker is defined as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class HelloWorldInvoker
{
    private System.Collections.Generic.List<Command> m_Log = new List<Command>();
    public HelloWorldInvoker()
    {
    }
    public void Execute(Command aCommand)
    {
        // Record the command in a log.
        m_Log.Add(aCommand);
        // Execute the command.
        aCommand.Execute();
    }
    public System.Collections.Generic.List<Command> GetLog()
    {
        return m_Log;
    }
}

The Invoker object includes an Execute() function, and usually, a SetCommand() function. However, in this example we have combined setting the command within the Execute() function. Note, we also include a logging mechanism within the Invoker. This allows us to keep a history of all commands executed by the Invoker. We then have the ability to repeat commands, undo commands, and log them.

With the Receiver and the Invoker defined, our objects now need some concrete commands to play with. We will create two types of commands for the HelloWorld example. One command will simply wrap a string and another command will wrap a string with a date.

Our Basic ConcreteCommand

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ConcreteCommand : Command
{
    protected string m_Text;
    public ConcreteCommand(IReceiver receiver, string strText)
        : base(receiver)
    {
        m_Text = strText;
    }
    public override void Execute()
    {
        receiver.Action(m_Text);
    }
    public override string GetCommandText()
    {
        return m_Text;
    }
}

This command is a basic wrapper for a string. When the Command is executed, its receiver will be passed a string for printing to the console.

Another Command, ConcreteDateCommand

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class ConcreteDateCommand : Command
{
    protected string m_Text;
    protected System.DateTime m_Date;
    public ConcreteDateCommand(IReceiver receiver, string strText)
        : base(receiver)
    {
        m_Text = strText;
        m_Date = System.DateTime.Now;
    }
    public override void Execute()
    {
        receiver.Action(m_Date.ToShortDateString() + " " + m_Text);
    }
    public override string GetCommandText()
    {
        return m_Date.ToShortDateString() + " " + m_Text;
    }
}

Notice, this implementation of the Command object includes an additional parameter of Date along with the string. The receiver still gets a string, but this time it is structured differently, to include a date. The important part is that the details are within the Command object.

Putting it All Together

Finally, we can write a main() program to put the Command design pattern to use. This example HelloWorld Command design pattern will create a Receiver, an Invoker, and a few commands to be used. The end result will be the Receiver acting upon the command by printing the string it receives.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static void Main(string[] args)
{
    HelloWorldPrinter MyPrinter = new HelloWorldPrinter();
    HelloWorldInvoker MyInvoker = new HelloWorldInvoker();
    // Create a basic Hello World command.
    Command MyCommand = new ConcreteCommand(MyPrinter, "Hello World");           
    // Execute the command. This will print "Hello World".
    MyInvoker.Execute(MyCommand);
    // Create a more complex Hello World command. This will print "1/1/2007 Ciao Mondo!"
    MyCommand = new ConcreteDateCommand(MyPrinter, "Ciao Mondo!");
    MyInvoker.Execute(MyCommand);
    Console.ReadKey();
}

The above program shows a basic implementation of the Command design pattern. However, surely we could perform the same functionality by just writing “Hello World” to the console. To show the power behind using the Command object, imagine a case where we wanted to log all commands executed, re-execute one of the commands, and finally print the entire log. We would first need a basic ShowLog() function. The function will take an Invoker (the class initiating all the commands), and read the history of all commands he has executed, as follows:

1
2
3
4
5
6
7
8
private static void ShowLog(HelloWorldInvoker MyInvoker)
{
    Console.WriteLine("Command Log:");
    foreach (Command aCommand in MyInvoker.GetLog())
    {
        Console.WriteLine("Executed Command: (" + aCommand.GetID() + ") " + aCommand.GetCommandText());
    }
}

Our main program can then be enhanced as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static void Main(string[] args)
{
    HelloWorldPrinter MyPrinter = new HelloWorldPrinter();
    HelloWorldInvoker MyInvoker = new HelloWorldInvoker();
    // Create a basic Hello World command.
    Command MyCommand = new ConcreteCommand(MyPrinter, "Hello World");          
    // Execute the command.
    MyInvoker.Execute(MyCommand);
    // Create a more complex Hello World command.
    MyCommand = new ConcreteDateCommand(MyPrinter, "Ciao Mondo!");
    MyInvoker.Execute(MyCommand);
    // Show all commands executed by our invoker. 2 commands are displayed.
    ShowLog(MyInvoker);
    Console.WriteLine("Re-executing the first command.");
    MyInvoker.Execute(MyInvoker.GetLog()[0]);
    // Since the Invoker re-executed a command, 3 commands are now displayed.
    ShowLog(MyInvoker);
    Console.ReadKey();
}

You can see the power behind the Command design pattern through the use of logging in the above example. Notice how the Invoker initially records 2 executed commands, then re-executes the first command, and finally contains a log of 3 executed commands.

Conclusion

The Command Object design pattern adds object-oriented design to a software command structure. It allows us to wrap a basic command in an object so we can then add parameters, speciailized processing, or log the command. By taking a look through your own C# ASP .NET projects, you may find a perfect scenerio for implementing the Command design pattern and enhancing your own projects.

About the Author

This article was written by Kory Becker, software developer and architect, skilled in a range of technologies, including web application development, machine learning, artificial intelligence, and data science.

Share