Chapter 22. The Command Pattern

The Chain of Responsibility forwards requests along a chain of classes, but the Command pattern forwards a request only to a specific object. It encloses a request for a specific action inside an object and gives it a known public interface. It lets you give the client the ability to make requests without knowing anything about the actual action that will be performed and allows you to change that action without affecting the client program in any way.

Motivation

When you build a C# user interface, you provide menu items, buttons, check boxes, and so forth to allow the user to tell the program what to do. When a user selects one of these controls, the program receives a clicked event, which it receives into a special routine in the user interface. Let’s suppose we build a very simple program that allows you to select the menu items File | Open, and File | Exit, and click on a button marked Red that turns the background of the window red. This program is shown in Figure 22-1.

Figure 22-1. A simple program that receives events from the button and menu items

image

The program consists of the File Menu object with the mnuOpen, and mnuExit MenuItems added to it. It also contains one button called btnRed. During the design phase, clicking on any of these items creates a little method in the Form class that gets called when the control is clicked.

As long as there are only a few menu items and buttons, this approach works fine, but when you have dozens of menu items and several buttons, the Form module code can get pretty unwieldy. In addition, we might eventually like the red command to be carried out both from the button and a menu item.

Command Objects

One way to ensure that every object receives its own commands directly is to use the Command pattern and create individual Command objects. A Command object always has an Execute() method that is called when an action occurs on that object. Most simply, a Command object implements at least the following interface.


public interface Command        {
      void Execute();
}

One objective of using this interface is to separate the user interface code from the actions the program must carry out, as shown here.


private void commandClick(object sender, EventArgs e) {
      Command comd = (Command)sender;
      comd.Execute ();
}

This event can be connected to every single user interface element that can be clicked, and each will contain its own implementation of the Execute method by simply deriving a new class from Button and MenuItem that supports this Command interface.

Then we can provide an Execute method for each object that carries out the desired action, thus keeping the knowledge of what to do inside the object where it belongs, instead of having another part of the program make these decisions.

One important purpose of the Command pattern is to keep the program and user interface objects completely separate from the actions that they initiate. In other words, these program objects should be completely separate from each other and should not have to know how other objects work. The user interface receives a command and tells a Command object to carry out whatever duties it has been instructed to do. The UI does not and should not need to know what tasks will be executed. This decouples the UI class from the execution of specific commands, making it possible to modify or completely change the action code without changing the classes containing the user interface.

The Command object can also be used when you need to tell the program to execute the command when the resources are available rather than immediately. In such cases, you are queuing commands to be executed later. Finally, you can use Command objects to remember operations so you can support Undo requests.

Building Command Objects

There are several ways to go about building Command objects for a program like this, and each has some advantages. We’ll start with the simplest one: creating new classes and implementing the Command interface in each. In the case of the button that turns the background red, we derive a RedButton class from Button and include an Execute method, satisfying the Command interface.


public class RedButton : System.Windows.Forms.Button, Command {
      //A Command button that turns the background red
      private System.ComponentModel.Container components = null;
      //-----
      public  void Execute() {
             Control c = this.Parent;
             c.BackColor =Color.Red ;
             this.BackColor =Color.LightGray  ;
      }
      public RedButton()         {
             InitializeComponent();
      }

In this implementation, we can deduce the background window by asking the button for its parent and setting that background to red. We could just as easily have passed the Form in as an argument to the constructor.

Remember, to create a class derived from Button that you can use in the IDE environment, you create a user control and change its inheritance from UserControl to Button and compile it. This adds an icon to the toolbox that you can drag onto the Form1 window.

To create a MenuItem that also implements the Command interface, you could use the MainMenu control on the toolbar and name it MenuBar. The designer is shown in Figure 22-2.

Figure 22-2. The menu designer interface

image

It is just as easy, however, to create the MainMenu in code as we see following. We derive the OpenMenu and ExitMenu classes from the MenuItem class.

However, we have to add these in the program code, since there is no way to add them in the Form Designer.


private void init() {
      //create a main menu and install it
      MainMenu main = new MainMenu();
      this.Menu =main;

      //create a click-event handler
      EventHandler evh = new EventHandler (commandClick);
      btRed.Click += evh;        //add to existing red button

      //create a "File" top level entry
      MenuItem file = new MenuItem("File");

      //create File Open command
      FileOpen mnflo = new FileOpen ();
      mnflo.Click += evh;        //add same handler
      main.MenuItems.Add ( file );

      //create a File-Exit command
      FileExit fex = new FileExit(this);
      file.MenuItems.AddRange( new MenuItem[]{ mnflo, fex} );
      fex.Click += evh;          //add same handler
}

Here is an example of the FileExit class.


public class FileExit :MenuItem, Command      {
      private Form form;
      //----------
      public FileExit(Form frm) :base ("Exit") {
             form = frm;
      }
      //----------
      public void Execute() {
             form.Close ();
      }
}

Then the File | Exit command will call it when you call that item’s Execute method. This certainly lets us simplify the user interface code, but it does require that we create and instantiate a new class for each action we want to execute.

Classes that require specific parameters to work need to have those parameters passed in the constructor or in a set method. For example, the File | Exit command requires that you pass it an instance of the Form object so the command can close it.


//create a File-Exit command
FileExit fex = new FileExit(this);

Consequences of the Command Pattern

The main disadvantage of the Command pattern seems to be a proliferation of little classes that clutter up the program. However, even in the case where we have separate click events, we usually call little private methods to carry out the actual function. It turns out that these private methods are just about as long as our little classes, so there is frequently little difference in complexity between building the Command classes and just writing more methods. The main difference is that the Command pattern produces little classes that are much more readable.

The CommandHolder Interface

While it is advantageous to encapsulate the action in a Command object, binding that object into the element that causes the action (such as the menu item or button) is not exactly what the Command pattern is about. Instead, the Command object really ought to be separate from the invoking client so you can vary the invoking program and the details of the command action separately. Rather than having the command be part of the menu or button, we can make the menu and button classes containers for a Command object that exists separately. We thus make these UI elements implement a CommandHolder interface.


public interface CommandHolder   {
      Command getCommand();
      void setCommand(Command cmd);
}

This simple interface says that there is a way to put a command object into a class and a way to retrieve it to execute it. This is particularly important where we have several ways of calling the same action, such as when we have both a Red button and a Red menu item. In such a case, you would certainly not want the same code to be executed inside both the MenuItem and the Button classes. Instead, you should fetch references to the same command object from both classes and execute that command.

Then we create CommandMenu class, which implements this interface.


public class CommandMenu : MenuItem, CommandHolder  {
      private Command command;
      public CommandMenu(string name):base(name)     {}
      //-----
      public void setCommand (Command comd) {
             command = comd;
      }
      //-----
      public Command getCommand () {
             return command;
      }
}

This actually simplifies our program. We don’t have to create a separate menu class for each action we want to carry out. We just create instances of the menu and pass them different labels and Command objects.

For example, our RedCommand object takes a Form in the constructor and sets its background to red in the Execute method.


public class RedCommand : Command       {
      private Control window;
      //-----
      public RedCommand(Control win)           {
             window = win;
      }
      //-----
      void Command.Execute () {
             window.BackColor =Color.Red ;
      }
}

We can set an instance of this command into both the RedButton and the red menu item objects, as we show below.


private void init() {
      //create a main menu and install it
      MainMenu main = new MainMenu();
      this.Menu =main;

      //create a click-event handler
      //note: btRed was added in the IDE
      EventHandler evh = new EventHandler (commandClick);
      btRed.Click += evh;        //add to existing red button
      RedCommand cRed = new RedCommand (this);
      btRed.setCommand (cRed);
      //create a "File" top level entry
      MenuItem file = new CommandMenu("File");
      main.MenuItems.Add ( file );

      //create File Open command
      CommandMenu mnuFlo = new CommandMenu("Open");
      mnuFlo.setCommand (new OpenCommand ());
      mnuFlo.Click += evh;              //add same handler

      //create a Red menu item, too
      CommandMenu mnuRed = new CommandMenu("Red");
      mnuRed.setCommand(cRed);
      mnuRed.Click += evh;              //add same handler

      //create a File-Exit command
      CommandMenu mnuFex = new CommandMenu("Exit");
      mnuFex.setCommand (new ExitCommand(this));
      file.MenuItems.AddRange(
             new CommandMenu[]{ mnuFlo, mnuRed, mnuFex} );
      mnuFex.Click += evh;              //add same handler
}

In the CommandHolder approach, we still have to create separate Command objects, but they are no longer part of the user interface classes. For example, the OpenCommand class is just this.


public class OpenCommand :Command       {
      public OpenCommand()
      {}
      public void Execute() {
             OpenFileDialog fd = new OpenFileDialog ();
             fd.ShowDialog ();
      }
}

Then our click event handler method needs to obtain the actual command object from the UI object that caused the action and execute that command.


private void commandClick(object sender, EventArgs e) {
      Command comd = ((CommandHolder)sender).getCommand ();
      comd.Execute ();
}

This is only slightly more complicated than our original routine and again keeps the action separate from the user interface elements. We can see this program in action in Figure 22-3.

Figure 22-3. Menu part of Command pattern using CommandHolder interface

image

We can see the relations between theses classes and interfaces clearly in the UML diagram in Figure 22-4.

Figure 22-4. Class diagram of CommandHolder approach

image

Providing Undo

Another of the main reasons for using Command design patterns is that they provide a convenient way to store and execute an Undo function. Each command object can remember what it just did and restore that state when requested to do so if the computational and memory requirements are not too overwhelming. At the top level, we simply redefine the Command interface to have three methods.


public interface Command     {
      void Execute();
      void Undo();
      bool isUndo();
}

Then we have to design each command object to keep a record of what it last did so it can undo it. This can be a little more complicated than it first appears, since having a number of interleaved Commands executed and then undone can lead to some hysteresis. In addition, each command will need to store enough information about each execution of the command that it will know what specifically has to be undone.

The problem of undoing commands is actually a multipart problem. First, you must keep a list of the commands that have been executed, and second, each command has to keep a list of its executions. To illustrate how we use the Command pattern to carry out undo operations, let’s consider the program shown in Figure 22-5 that draws successive red or blue lines on the screen, using two buttons to draw a new instance of each line. You can undo the last line you drew with the undo button.

Figure 22-5. A program that draws red and blue lines each time you click the Red and Blue buttons

image

If you click on Undo several times, you’d expect the last several lines to disappear no matter what order the buttons were clicked in, as shown in Figure 22-6.

Figure 22-6. The same program as in Figure 22-5 after the Undo button has been clicked several times

image

Thus, any undoable program needs a single sequential list of all the commands that have been executed. Each time we click on any button, we add its corresponding command to the list.


private void commandClick(object sender, EventArgs e) {
      //get the command
      Command comd = ((CommandHolder)sender).getCommand ();
      undoC.add (comd);   //add to undo list
      comd.Execute ();    //and execute it
}

Further, the list to which we add the Command objects is maintained inside the Undo command object so it can access that list conveniently.


public class UndoComd:Command   {
      private ArrayList undoList;
      public UndoComd()   {
             undoList = new ArrayList ();
      }
      //-----
      public void add(Command comd) {
             if(! comd.isUndo ()) {
                    undoList.Add (comd);
             }
      }
      //-----
      public bool isUndo() {
             return true;
      }
      //-----
      public void Undo() {              }
      //-----
      public void Execute() {
             int index = undoList.Count - 1;
             if (index >= 0) {
                    Command cmd = (Command)undoList[index];
                    cmd.Undo();
                    undoList.RemoveAt(index);
             }
      }
}

The undoCommand object keeps a list of Commands, not a list of actual data. Each command object has its unDo method called to execute the actual undo operation. Note that since the undoCommand object implements the Command interface, it, too, needs to have an unDo method. However, the idea of undoing successive unDo operations is a little complex for this simple example program. Consequently, you should note that the add method adds all Commands to the list except the undoCommand itself, since we have just defined undoing an unDo command as doing nothing. For this reason, our new Command interface includes an isUndo method that returns false for the RedCommand and BlueCommand objects and true for the UndoCommand object.

The RedCommand and BlueCommand classes simply use different colors and start at opposite sides of the window, although both implement the revised Command interface. Each class keeps a list of lines to be drawn in a Collection as a series of DrawData objects containing the coordinates of each line. Undoing a line from either the red or the blue line list simply means removing the last DrawData object from the drawList collection. Then either command forces a repaint of the screen. Since they do the same thing except for the color of the line and its diagonal direction, we can create a ColorCommand base class and derive our RedCommand and BlueCommand classes from it. Here is the base ColorCommand class.


public class ColorCommand :Command
      {
             protected Color color;            //line color
             protected PictureBox pbox;        //box to draw in
             protected ArrayList drawList;     //list of lines
             protected int x, y, dx, dy;       //coordinates
             //------
             public ColorCommand(PictureBox pict)
      {
             pbox = pict; //copy in picture box
             drawList = new ArrayList ();     //create list
      }
      //------
      public void Execute() {
             //create a new line to draw
             DrawData dl = new DrawData(x, y, dx, dy);
             drawList.Add(dl);   //add it to the list
             x = x + dx;         //increment the positions
             y = y + dy;
             pbox.Refresh();
      }
      //-----
      public bool isUndo() {
             return false;
      }
      //-----
      public void Undo() {
             DrawData dl;
             int index = drawList.Count - 1;
             if (index >= 0) {
                    dl = (DrawData)drawList[index];
                    drawList.RemoveAt(index);
                    x = dl.getX();
                    y = dl.getY();
             }
             pbox.Refresh();
      }
      //-----
      public void draw(Graphics g) {
             Pen rpen = new Pen(color, 1);
             int h = pbox.Height;
             int w = pbox.Width;
      //draw all the lines in the list
             for (int i = 0; i < drawList.Count ; i++) {
                    DrawData dl = (DrawData)drawList[i];
                    g.DrawLine(rpen, dl.getX(), dl.getY(),
                           dl.getX() + dx, dl.getDy() + h);
             }
      }
}

Note that the draw method in the drawCommand class redraws the entire list of lines the command object has stored. These two draw methods are called from the paint handler of the form.


public void paintHandler(object sender, PaintEventArgs e) {
      Graphics g = e.Graphics ;
      blueC.draw(g);
      redC.draw (g);
}

We can then derive the BlueCommand and RedCommand classes from the ColorCommand class. Here is the BlueCommand class.


public class BlueCommand :ColorCommand {
      public BlueCommand(PictureBox pict):base(pict) {
             color = Color.Blue ;
             x = pbox.Width ;
             dx = -20;
             y = 0;
             dy = 0;
      }
}

We can create the RedCommand just as easily.


public class RedCommand : ColorCommand  {
      public RedCommand(PictureBox pict):base(pict){
             color = Color.Red;
             x = 0;
             dx = 20;
             y = 0;
             dy = 0;
      }
}

The set of classes we use in this Undo program is shown in Figure 22-7.

Figure 22-7. The classes used to implement Undo in a Command pattern implementation

image

Thought Questions

1. Mouse clicks on list box items and on radio buttons also constitute commands. Clicks on multiselect list boxes could also be represented as commands. Design a program including these features.

2. A lottery system uses a random number generator constrained to integers from 1 to 50. The selections are made at intervals selected by a random timer. Each selection must be unique. Design command patterns to choose the winning numbers each week.

Programs on the CD-ROM

image

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset