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.
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.
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.
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:
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.
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.
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.
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 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:
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.
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.
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.
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.
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:
Our main program can then be enhanced as follows:
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.
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.
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.