A Comprehensive Pattern-Based Example


The Command Pattern

The Command pattern is used to 1) decouple the knowledge of a particular action from an object that needs the action carried out, and 2) to encapsulate the action in the form of an object.

The Command pattern manifests itself to a certain degree in the Component/ActionListener relationship as you saw in the previous section. Components such as JButtons can add any number of ActionListeners. When a button is clicked an ActionEvent object is created and passed as an argument to an ActionListener’s actionPerformed() method. If only one ActionListener exists to handle all button clicks then it’s the responsibility of the actionPerformed() method to determine the source component and perform the necessary actions. As was noted above, in large application this can lead to a large actionPerformed() method.

Another approach to implementing the Command pattern in Java that you see frequent examples of is to extend components like JButton and JMenuItem and make them commands. I don’t like this approach because it violates Coad’s criteria. (i.e., Is a Command really a JButton?)

The Command pattern implementation strategy I prefer is to combine the Dynamic Factory pattern with a separate Command class hierarchy. At the root of the hierarchy is an abstract class I will call BaseCommand and is given in example 25.12.

Example 25.12: BaseCommand.java

image from book
 1     package com.pulpfreepress.commands; 2 3     import com.pulpfreepress.interfaces.*; 4 5     public abstract class BaseCommand { 6 7        protected static iModel   its_model = null; 8        protected static iView    its_view = null; 9 10       public void setModel(iModel model){ 11         if(its_model == null){ 12            its_model = model; 13         } 14       } 15 16       public void setView(iView view){ 17         if(its_view == null){ 18            its_view = view; 19         } 20       } 21 22       public abstract void execute(); // must be implemented in derived classes 23 24    } // end BaseCommand class definition
image from book

Referring to example 25-12 — The BaseCommand class contains two protected static fields of type iModel and iView. These fields are protected so that subclasses can access them directly. Two methods setModel() and setView() initialize the its_model and its_view references accordingly. There is one abstract method named execute(). This method must be implemented by derived classes. It is the concrete command classes that implement the actions unique to each command. Some commands may interact with the model only while others may interact only with the view. Other commands may execute actions having nothing to do with the model or the view.

This approach to the Command pattern preserves the relationship between the MVC components as presented earlier but greatly simplifies the Controller’s actionPerformed() method as is shown in example 25.13.

Example 25.13: Controller.java

image from book
 1     package com.pulpfreepress.controller; 2 3     import com.pulpfreepress.utils.*; 4     import com.pulpfreepress.commands.*; 5     import com.pulpfreepress.exceptions.*; 6     import com.pulpfreepress.model.*; 7     import com.pulpfreepress.view.*; 8     import com.pulpfreepress.interfaces.*; 9     import java.awt.event.*; 10 11    public class Controller implements ActionListener { 12 13       private CommandFactory command_factory = null; 14       private iModel its_model; 15       private iView its_view; 16 17       public Controller(){ 18         command_factory = CommandFactory.getInstance(); 19         its_model = new Model(); 20         its_view = new View(this); 21       } 22 23 24      public void actionPerformed(ActionEvent ae){ 25         try{ 26         BaseCommand command = command_factory.getCommand(ae.getActionCommand()); 27         command.setModel(its_model); 28         command.setView(its_view); 29         command.execute(); 30         }catch(CommandNotFoundException cnfe){ 31            System.out.println("Command not found!"); 32         } 33      } 34 35      public static void main(String[] args){ 36         new Controller(); 37      } // end main() method 38    } // end Controller class definition
image from book

Referring to example 25.13 — the Controller class presented here is used in the comprehensive example presented in the next section so bear with me. Let’s focus on the actionPerformed() method beginning on line 24. The command_factory reference is used to dynamically load and create an instance of a command based on the name of the clicked component’s action command. (A component’s action command is, by default, the string associated with the component’s label. For example, given a JButton with a label “Submit” its action command string is by default “Submit”.) Once the command is created the setModel(), setView(), and execute() methods are called. So long as the command class exists and is in the system classpath things will work as expected.

Example 25.14 gives the code for the CommandFactory class.

Example 25.14: CommandFactory.java

image from book
 1     package com.pulpfreepress.utils; 2 3     import com.pulpfreepress.commands.*; 4     import com.pulpfreepress.exceptions.*; 5 6     public class CommandFactory { 7 8     private static CommandFactory command_factory_instance = null; 9     private static CommandProperties command_properties = null; 10 11    static { 12       command_properties = CommandProperties.getInstance(); 13    } 14 15 16    private CommandFactory(){ } 17 18    public static CommandFactory getInstance(){ 19      if(command_factory_instance == null){ 20        command_factory_instance = new CommandFactory(); 21      } 22      return command_factory_instance; 23    } 24 25    public BaseCommand getCommand(String command_string) throws CommandNotFoundException { 26       BaseCommand command = null; 27       if(command_string == null){ 28          throw new CommandNotFoundException( command_string + " command class not found!"); 29       }  else{ try{ 30            String command_classname = command_properties.getProperty(command_string); 31            Class command_class = Class.forName(command_classname); 32            Object command_object = command_class.newInstance(); 33            command = (BaseCommand) command_object; 34            }catch(Throwable t){ 35              t.printStackTrace(); 36              throw new CommandNotFoundException(t.toString(), t); 37             } 38        } // end else 39         return command; 40      } // end getCommand() method 41 42    } // end CommandFactory class definition
image from book

Referring to example 25.14 — The CommandFactory implements the Singleton pattern. It also utilizes the services of the CommandProperties class which was presented earlier in example 25.1. The component action command strings are mapped to their respective command classes in the image from book Command.properties file which I will show you shortly. Most of the work of this class is done in the getCommand() method. Here the command_string argument is used to look up the class name of the command class that will actually perform the work. If the class exists it is loaded. If it loads successfully an instance of the class is created and returned.

Example 25.15 shows the contents of the image from book command.properties file.

Example 25.15: Command.properties file contents

image from book
 1     #CommandProperties File - Edit Carefully 2     #Sat Jun 18 12:24:38 EDT 2005 3     PROPERTIES_FILE=Command.properties 4     Load=com.pulpfreepress.commands.LoadEmployeesCommand 5     Exit=com.pulpfreepress.commands.ApplicationExitCommand 6     NewSalariedEmployee=com.pulpfreepress.commands.NewSalariedEmployeeCommand 7     EditEmployee=com.pulpfreepress.commands.EditEmployeeCommand 8     Sort=com.pulpfreepress.commands.SortEmployeesCommand 9     DeleteEmployee=com.pulpfreepress.commands.DeleteEmployeeCommand 10    List=com.pulpfreepress.commands.ListEmployeesCommand 11    NewHourlyEmployee=com.pulpfreepress.commands.NewHourlyEmployeeCommand 12    Save=com.pulpfreepress.commands.SaveEmployeesCommand
image from book

Referring to example 25.15 — notice how command names like Load are mapped to their respective class handler.

Quick Review

The Command pattern is used to 1) decouple the knowledge of a particular action from an object that needs the action carried out, and 2) to encapsulate the action in the form of an object. The Command pattern can be combined with the Dynamic Factory pattern to map command names to class handlers and dynamically load and execute the command handler.




Java For Artists(c) The Art, Philosophy, and Science of Object-Oriented Programming
Java For Artists: The Art, Philosophy, And Science Of Object-Oriented Programming
ISBN: 1932504052
EAN: 2147483647
Year: 2007
Pages: 452

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net