Click here to Skip to main content
16,017,238 members
Articles / Programming Languages / C#

The Command Pattern and MVC Architecture

Rate me:
Please Sign up or sign in to vote.
4.86/5 (40 votes)
24 Nov 2005CPOL10 min read 280K   2.1K   190   44
A demonstration of how to use the Command Pattern in real-world code.

Introduction

One of the more frequently used ‘Gang of Four’ design patterns is the Command Pattern. This pattern has nothing to do with ADO.NET and everything to do with communication between the UI and the other tiers of a multi-tier application. The pattern provides several benefits to an application; most significantly, an unlimited undo/redo capability.

The application attached to this article demonstrates how to use the Command Pattern in real-world code. I wrote the application as a learning exercise, and I invite comments from readers about the implementation of the pattern, and about best practices that might be incorporated into the program.

The Calculator Application

The application emulates a simple four-function RPN calculator:

‘RPN’ stands for ‘Reverse Polish Notation’. Anyone who has used an HP-12C financial calculator knows how it works; for everyone else, here is a brief explanation:

RPN calculators do not have an ‘=’ key. To perform a calculation, the first number is added to the calculator’s register using the ‘+’ button. Then the second number is entered, and the desired operation is selected by clicking the appropriate button. For example, to multiply 2 x 3, the user clicks the following buttons:

  • Click ‘2’, then ‘+’: The number ‘2’ appears in the display.
  • Click ‘3’, then ‘x’: The number ‘6’ appears in the display.

Calculations can be chained by entering the next number, then clicking the desired operation button. For example, to add 2 to the above result, click ‘2’, then ‘+’. The number ‘8’ will appear in the display.

The calculator has ‘Clear’ (‘C’) and ‘Correct Error’ (‘CE’) keys. The ‘C’ key clears the register (resets it to zero), and the ‘CE’ key removes the last digit entered in the display.

It also has ‘Undo’ and ‘Redo’ buttons. Each time the ‘Undo’ button is clicked, it undoes an operation, starting with the most recent. The ‘Redo’ button redoes undone operations. The calculator supports unlimited Undo and Redo operations.

Here’s an interesting feature about Undo and Redo: Let’s say I enter 10 operations into the calculator, and then undo four of them. I can redo all four undone operations by clicking the ‘Redo’ button four times. Or, instead of redoing operations, I can enter a new operation. If I enter a new operation, it cuts off my ability to restore the four undone operations, because it renders the state of the calculator’s register inconsistent with the undone operations.

So, I lose the four undone operations. However, after entering the new operation, it is undoable, and the six operations that came before it are also undoable. In other words, entering a new operation destroys any undoes that might otherwise be redone, but it preserves all history that has not been undone.

Finally, the calculator has a ‘log’ field, similar to a paper tape, which records the sequence of calculations, including Undo and Redo operations.

How the Command Pattern Fits In

It is an almost trivial task to emulate a simple RPN calculator. A form, some buttons, a button handler for each one, and a couple of text boxes, and we’re done. The code amounts to simple addition, subtraction, and so on. It shouldn’t run to more than a few lines, at most.

Adding Undo and Redo capabilities complicates things. I might be able to add the ability to undo the last number entered by adding a variable or two, but suppose I want to add support for unlimited Undo and Redo operations? If the user enters 100, or even 1000 operations, they should be able to click the Undo button 100 or 1000 times to undo them all, then click the Redo button the same number of times to redo them all.

That looks like a daunting task, but the Command Pattern makes it surprisingly easy. Basically, what we do is wrap each command in an object, which we will add to a list (which we call the History list) when the command is executed. Then, to undo or redo, we simply move up and down that list.

How Model-View-Controller Fits In

The demo application implements the command in a Model-View-Controller framework. The demo application’s classes are named to match this architecture.

The View has the responsibility of managing the UI—get user input and display results. The Model contains the business rules for our calculator—the code that describes ‘how to act like a calculator’. The Controller sits between the View and the Model. In the demo app, the Controller acts as a command processor. Its primary responsibility is to maintain the History list that enables Undo/Redo functionality.

Without the Command pattern, the Controller would have to act as a sort of traffic cop between the View and the Model. It would receive requests from the View and direct them to the appropriate methods of the Controller. The benefit of using a traffic cop is that it isolates the View and the Model from each other. The View doesn’t have to know about the Model—it can behave in a ‘Viewish’ manner. And the model doesn’t have to know anything about the View—it can behave in a ‘Modelish’ manner. The Controller stands between them, interpreting requests and (sometimes) results.

In most MVC implementations, there is a notable exception to this principle of isolation. The Model typically fires events to announce its state changes, and passes along appropriate information about the state change in event arguments. The View can subscribe directly to these events to update itself, without going through the Controller. This architecture takes some of the load off the Controller.

How the Command Pattern and MVC Work Together

The Command Pattern takes more of the load from the Controller. Each Command object knows which method to call on the Model to do its work, and it knows which method to call to undo its work. So, instead of the Controller having to know how to route requests from the View, it need only call the Command object’s Execute() method. This approach keeps the Controller from becoming overburdened and frees it for other work, in this case managing the History list.

How the Command Pattern Is Implemented

The demo application implements the Command Pattern in a relatively straightforward manner. Here’s the UML representation of the classic GoF Command Pattern:

Since the demo application has only one form in its UI, the program locates the Application functions in View.cs. In a more complex app with multiple forms, this functionality would be stripped out of the UI and located in a separate class.

In the Command Pattern, the Client (the Application class) instantiates a concrete Command object to represent a request to be made of a Receiver. The concrete Command is passed to an Invoker (such as a button-click event handler) as the object’s base type. In other words, we have a concrete Command class for each request that might come from the UI.

In the demo application, we have a CommandAdd class, a CommandSubtract class, and so on. All of these classes are derived from the abstract Command class. Each has its own implementation of the Execute() and the Undo() methods specified in the base class. Since these classes have the same methods, implemented differently, we can pass them back and forth as their base type.

That means the buttons that create the classes, and the Controller that uses them, see all these different objects as being of the same type—Command. That’s how we can store different types of objects in the same History list without having to figure out the specific type we are dealing with. It is the essence of polymorphism.

The Client assigns each Command object to an Invoker object. In the demo application, the invokers are all .NET Button objects. The Command objects are ‘wired’ to the buttons by means of event handlers. For example:

C#
private void buttonAdd_Click(object sender, EventArgs e)
{
    int operand = Convert.ToInt32(textBoxDisplay.Text);
    Command commandAdd = new CommandAdd(operand, m_Model);
    m_Controller.ExecuteCommand(commandAdd);
}

When the ‘+’ button is clicked, the buttonAdd_Click() event handler is called. The handler creates a new CommandAdd object, declaring it as its base type. Then, the handler sends the Command object to the Controller with instructions to execute the command.

The Controller executes the command as follows:

C#
public void ExecuteCommand(Command command)
{
    // Purge history list
    this.TrimHistoryList();

    // Execute the command and add it to the history list
    command.Execute();
    m_HistoryList.Add(command);

    // Move the 'next undo' pointer to point to the new command
    m_NextUndo++;
}

First, the Controller calls its TrimHistoryList() method to clear any undone items from the history list. That’s because executing the Command will cut off our ability to redo those commands. Then, it calls the Command object’s Execute() method. After that, it adds the Command object to the History list. And finally, it increments its ‘Next Undo’ pointer, so that it will point to the newly-added Command object.

When the Controller calls the Command object’s Execute() method, that method calls the method in the Model that will accomplish the task that needs to be done:

C#
public override void Execute()
{
    m_Model.Add(m_Operand);
}

That’s how the request gets routed from the View, through the Controller, to the Model. The Command object has assumed the ‘traffic cop’ responsibility that would otherwise fall on the Controller.

The method called on the Model performs the calculation needed, creates a log entry for the on-screen log field, and fires a DisplayChange event. Here is an example from the Model class:

C#
internal void Add(int operand)
{
    p_Register += operand;
    string logEntry = String.Format("Added {0} to Register." + 
           "\r\n\tRegister = {1}\r\n\r\n", operand, p_Register);
    this.FireDisplayChangeEvent(p_Register, logEntry, true);
}

When the DisplayChange event fires, the View (which subscribes to the event) runs its DisplayChange event handler, which updates the Display and Log fields.

Undo and Redo

Even without undo and redo capabilities, the Command Pattern is a useful participant in an MVC framework, because of the load it takes off the Controller. But Undo and Redo capabilities are probably the reason most developers think of the pattern.

Implementing undo is rather simple. When the ‘Undo’ button is clicked, its event handler sends an ‘Undo’ message to the Controller:

C#
private void buttonUndo_Click(object sender, EventArgs e)
{
    /* Note that we don't create a Command object for Undo.
     * That's because it wouldn't make sense to undo an undo.
     * We could simply redo the action that was undone. Nor 
     * would it make sense to redo an undo. */

    m_Controller.Undo();
}

As the code notes, we don’t use a Command object for this request, because it would not make sense.

When the Controller receives the request, it undoes the last operation added to the History list:

C#
public void Undo()
{
    // If NextUndo < 0, there are no commands to undo
    if (m_NextUndo < 0) return;

    // Get the Command object to be undone
    Command command = m_HistoryList[m_NextUndo];

    // Execute the Command object's Undo method
    command.Undo();

    // Move the pointer up one item
    m_NextUndo--;
}

All it really has to do is call the Command object’s Undo() method; the rest of the work is housekeeping for the History list.

In the demo application, a Command object’s Undo function simply calls the inverse operation of the one called in the Execute() function. If Execute() calls Model.Add(), then Undo will call Model.Subtract(). The Undo() method uses the same operand as was used in the Execute() method; this value is stored as a member variable of the Command object.

When a command is undone, it is not removed from the History list. Instead, a ‘Next Undo’ pointer is decremented to point to the previous item in the History list. This approach makes redo operations easy. At any point in time, the first command to be redone is the one after that which ‘Next Undo’ points to. So we simply get that item, call its Execute() method, and increment the ‘Next Undo’ pointer, so that it points to the redone item. In short, Undo and Redo are simple matters of traversing up and down the History list.

Advantages and Disadvantages of the Command Pattern

The main benefits of the Command Pattern are discussed above. The major disadvantage of the pattern is that it results in lots of little Command classes that can clutter up a design. However, the routing information that Command objects encapsulate has to go somewhere. If this information is not contained in Command objects, then it will have to go into the Controller. The resulting bloat may necessitate partitioning the Controller into a subsystem, and it will certainly make the Controller harder to understand and maintain.

A related benefit of the Command class is that it makes it easier to add new functions to an application. If we add a new capability, we no longer need to reopen the Controller class to code routing information. Instead, we encapsulate the information in a Command object and assign it to whatever control we use in the View to invoke the new capability. When the Command gets passed to the Controller, the Controller can use it to route the request, without knowing anything about it. If you remember The Matrix, it’s almost as cool as Trinity downloading the knowledge of how to fly a helicopter.

Call for Comments

As I noted at the beginning of this article, I developed the demo application as a learning exercise in MVC and the Command Pattern. I would very much like to see comments from other developers who have addressed the same issues. Have you approached the problem differently? What have I missed? What would you consider to be best practices in the area? What other advantages and disadvantages do you see in the pattern? Thanks for your feedback; it is genuinely appreciated.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Foresight Systems
United States United States
David Veeneman is a financial planner and software developer. He is the author of "The Fortune in Your Future" (McGraw-Hill 1998). His company, Foresight Systems, develops planning and financial software.

Comments and Discussions

 
QuestionQuestion about references ... if any Pin
Ramsin19-Jul-13 12:32
Ramsin19-Jul-13 12:32 
QuestionDid you write this article on a Mac? Pin
Wayne B18-Aug-10 7:15
Wayne B18-Aug-10 7:15 
AnswerRe: Did you write this article on a Mac? Pin
David Veeneman20-Aug-10 5:18
David Veeneman20-Aug-10 5:18 
GeneralPassing Model and Controller from outside the View [modified] Pin
bilo8114-Oct-08 23:29
bilo8114-Oct-08 23:29 
GeneralMVC in frameworks Pin
taras.di30-Jun-08 0:23
taras.di30-Jun-08 0:23 
GeneralRe: MVC in frameworks Pin
David Veeneman30-Jun-08 1:20
David Veeneman30-Jun-08 1:20 
GeneralRe: MVC in frameworks Pin
taras.di30-Jun-08 1:55
taras.di30-Jun-08 1:55 
GeneralIntegrity of the Model Pin
luedi6-Jun-08 0:22
luedi6-Jun-08 0:22 
GeneralRe: Integrity of the Model Pin
David Veeneman6-Jun-08 2:16
David Veeneman6-Jun-08 2:16 
GeneralRe: Integrity of the Model Pin
taras.di30-Jun-08 0:09
taras.di30-Jun-08 0:09 
GeneralRe: Integrity of the Model Pin
David Veeneman30-Jun-08 1:26
David Veeneman30-Jun-08 1:26 
QuestionCommand &MVC Pattern Pin
guofuchen5-Dec-07 14:10
guofuchen5-Dec-07 14:10 
GeneralGreat article and app Pin
abc@tempinbox.com20-Aug-07 21:02
abc@tempinbox.com20-Aug-07 21:02 
QuestionSimlpe but Great article Pin
QMasters5-May-07 22:27
professionalQMasters5-May-07 22:27 
AnswerRe: Simlpe but Great article Pin
David Veeneman6-May-07 5:39
David Veeneman6-May-07 5:39 
GeneralUndo and redo Pin
Puchko Vasili15-Feb-07 3:51
Puchko Vasili15-Feb-07 3:51 
GeneralUndo and Redo of non inverse data Pin
Cohen Shwartz Oren9-Feb-07 11:56
Cohen Shwartz Oren9-Feb-07 11:56 
GeneralRe: Undo and Redo of non inverse data Pin
David Veeneman9-Feb-07 15:41
David Veeneman9-Feb-07 15:41 
GeneralRe: Undo and Redo of non inverse data Pin
baxissimo29-May-07 21:27
baxissimo29-May-07 21:27 
QuestionQuestion regarding view and model Pin
mikeperetz27-Jul-06 15:49
mikeperetz27-Jul-06 15:49 
AnswerRe: Question regarding view and model Pin
David Veeneman9-Feb-07 16:13
David Veeneman9-Feb-07 16:13 
GeneralRe: Question regarding view and model Pin
Cohen Shwartz Oren10-Feb-07 23:51
Cohen Shwartz Oren10-Feb-07 23:51 
GeneralRe: Question regarding view and model Pin
David Veeneman11-Feb-07 2:59
David Veeneman11-Feb-07 2:59 
GeneralThe Next Step Pin
David Veeneman13-Apr-06 11:14
David Veeneman13-Apr-06 11:14 
GeneralCommand & MVC Patterns Pin
rugbyhead9-Apr-06 15:23
rugbyhead9-Apr-06 15:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.