[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Java Tip 68: Learn how to implement the Command pattern in Java - JavaWorld - February 1999



The attached article, from JavaWorld e-zine, proposes a coding style for
passing 'Commands' between active Java objects. Although I've only had
time to skim-read the article, it seems we've been here before!

See http://www.javaworld.com/javatips/jw-javatip68.html?020899txt

Does anyone have the time to write and contribute a similar article,
proposing JCSP multithreaded programming to achieve the same ends? It
would be a technically attractive solution, not least because the
attached article implies a distinction between active and passive
objects, as described by Peter Welch, without actually recognising this
distinction.

A possible approach might be to build on the command object technique as
a means to structure data passing via JCSP channels.

Rick
--
Richard Beton B.Sc. C.Phys. M.Inst.P.
Roke Manor Research Limited (http://www.roke.co.uk/)
--------- Standard Disclaimer about my own views etc etc --------
---------  My mail client accepts rich text (HTML) mail  --------
Welsh Highland Railway: http://www.whr.co.uk/WHR/WHR.html

Title: Java Tip 68: Learn how to implement the Command pattern in Java - JavaWorld - February 1999
February 1999 JavaWorld Get FREE JW e-mail alerts
[JavaWorld Main Page] [JavaWorld Search] [Nuts & Bolts] [News & Views] [Java Resources]

Click on our Sponsors to help Support JavaWorld

Java Tip 68: Learn how to implement the Command pattern in Java

Add flexibility and extensibility to your programs with this object-oriented equivalent of the callback

Summary
Sometimes it's necessary to issue requests to objects without knowing anything about the operation being requested or the receiver of the request. In procedural languages, this type of communication is accomplished via a callback: a function that is registered somewhere to be called at a later point. Commands are the object-oriented equivalent of callbacks and encapsulate the callback function. This Java Tip demonstrates how to implement the Command pattern in Java. (1,070 words)

By Bala Paranj


Mail this
article to
a friend

Supporting
sponsors

Design patterns not only accelerate the design phase of an object-oriented (OO) project but also increase the productivity of the development team and quality of the software. A Command pattern is an object behavioral pattern that allows us to achieve complete decoupling between the sender and the receiver. (A sender is an object that invokes an operation, and a receiver is an object that receives the request to execute a certain operation. With decoupling, the sender has no knowledge of the Receiver's interface.) The term request here refers to the command that is to be executed. The Command pattern also allows us to vary when and how a request is fulfilled. Therefore, a Command pattern provides us flexibility as well as extensibility.

In programming languages like C, function pointers are used to eliminate giant switch statements. (See "Java Tip 30: Polymorphism and Java" for a more detailed description.) Since Java doesn't have function pointers, we can use the Command pattern to implement callbacks. You'll see this in action in the first code example below, called TestCommand.java.

Developers accustomed to using function pointers in another language might be tempted to use the Method objects of the Reflection API in the same way. For example, in his article "Java Reflection," Paul Tremblett shows you how to use Reflection to implement transactions without using switch statements. I've resisted this temptation, since Sun advises against using the Reflection API when other tools more natural to the Java programming language will suffice. (See Resources for links to Tremblett's article and for Sun's Reflection tutorial page.) Your program will be easier to debug and maintain if you don't use Method objects. Instead, you should define an interface and implement it in the classes that perform the needed action.

Therefore, I suggest you use the Command pattern combined with Java's dynamic loading and binding mechanism to implement function pointers. (For details on Java's dynamic loading and binding mechanism, see James Gosling and Henry McGilton's "The Java Language Environment -- A White Paper," listed in Resources.)

By following the above suggestion, we exploit the polymorphism provided by the application of a Command pattern to eliminate giant switch statements, resulting in extensible systems. We also exploit Java's unique dynamic loading and binding mechanisms to build a dynamic and dynamically extensible system. This is illustrated in the second code sample example below, called TestTransactionCommand.java.

The Command pattern turns the request itself into an object. This object can be stored and passed around like other objects. The key to this pattern is a Command interface, which declares an interface for executing operations. In its simplest form, this interface includes an abstract execute operation. Each concrete Command class specifies a receiver-action pair by storing the Receiver as an instance variable. It provides different implementations of the execute() method to invoke the request. The Receiver has the knowledge required to carry out the request.

Figure 1 below shows the Switch -- an aggregation of Command objects. It has flipUp() and flipDown() operations in its interface. Switch is called the invoker because it invokes the execute operation in the command interface.

The concrete command, LightOnCommand, implements the execute operation of the command interface. It has the knowledge to call the appropriate Receiver object's operation. It acts as an adapter in this case. By the term adapter, I mean that the concrete Command object is a simple connector, connecting the Invoker and the Receiver with different interfaces.

The client instantiates the Invoker, the Receiver, and the concrete command objects.


Figure 2, the sequence diagram, shows the interactions between the objects. It illustrates how Command decouples the Invoker from the Receiver (and the request it carries out). The client creates a concrete command by parameterizing its constructor with the appropriate Receiver. Then it stores the Command in the Invoker. The Invoker calls back the concrete command, which has the knowledge to perform the desired Action() operation.


The client (main program in the listing) creates a concrete Command object and sets its Receiver. As an Invoker object, Switch stores the concrete Command object. The Invoker issues a request by calling execute on the Command object. The concrete Command object invokes operations on its Receiver to carry out the request.

The key idea here is that the concrete command registers itself with the Invoker and the Invoker calls it back, executing the command on the Receiver.

Command pattern example code
Let's take a look at a simple example illustrating the callback mechanism achieved via the Command pattern.

The example shows a Fan and a Light. Our objective is to develop a Switch that can turn either object on or off. We see that the Fan and the Light have different interfaces, which means the Switch has to be independent of the Receiver interface or it has no knowledge of the code>Receiver's interface. To solve this problem, we need to parameterize each of the Switchs with the appropriate command. Obviously, the Switch connected to the Light will have a different command than the Switch connected to the Fan. The Command class has to be abstract or an interface for this to work.

When the constructor for a Switch is invoked, it is parameterized with the appropriate set of commands. The commands will be stored as private variables of the Switch.

When the flipUp() and flipDown() operations are called, they will simply make the appropriate command to execute( ). The Switch will have no idea what happens as a result of execute( ) being called.

TestCommand.java

class Fan {
        public void startRotate() {
                System.out.println("Fan is rotating");
        }
        public void stopRotate() {
                System.out.println("Fan is not rotating");
        }
}
class Light {

        public void turnOn( ) {
                System.out.println("Light is on ");
        }
        public void turnOff( ) {
                System.out.println("Light is off");
        }
}
class Switch {
        private Command UpCommand, DownCommand;

        public Switch( Command Up, Command Down) {
                UpCommand = Up; // concrete Command registers itself with the invoker 
                DownCommand = Down;
        }
        void flipUp( ) { // invoker calls back concrete Command, which executes the Command on the receiver 
                        UpCommand . execute ( ) ;							
        }
        void flipDown( ) {
                        DownCommand . execute ( );
        }
}

class LightOnCommand implements Command {

        private Light myLight;

        public LightOnCommand ( Light L) {
                myLight  =  L;
        }
        public void execute( ) {
                myLight . turnOn( );
        }
        
}
class LightOffCommand implements Command {

        private Light myLight;

        public LightOffCommand ( Light L) {
                myLight  =  L;
        }
        public void execute( ) {
                myLight . turnOff( );
        }
        
}
class FanOnCommand implements Command {

        private Fan myFan;

        public FanOnCommand ( Fan F) {
                myFan  =  F;
        }
        public void execute( ) {
                myFan . startRotate( );
        }
        
}
class FanOffCommand implements Command {

        private Fan myFan;

        public FanOffCommand ( Fan F) {
                myFan  =  F;
        }
        public void execute( ) {
                myFan . stopRotate( );
        }
        
}

public class TestCommand {
                public static void main(String[] args) {
                        Light  testLight = new Light( );
                        LightOnCommand testLOC = new LightOnCommand(testLight);
                        LightOffCommand testLFC = new LightOffCommand(testLight);
                        Switch testSwitch = new Switch( testLOC,testLFC);       
                        testSwitch.flipUp( );
                        testSwitch.flipDown( );
                        
                        Fan testFan = new Fan( );
                        FanOnCommand foc = new FanOnCommand(testFan);
                        FanOffCommand ffc = new FanOffCommand(testFan);
                        Switch ts = new Switch( foc,ffc);
                        ts.flipUp( );
                        ts.flipDown( ); 

                }
}               

Command.java

public interface Command {
        public abstract void execute ( );
}

Notice in the code example above that the Command pattern completely decouples the object that invokes the operation -- (Switch ) -- from the ones having the knowledge to perform it -- Light and Fan. This gives us a lot of flexibility: the object issuing a request must know only how to issue it; it doesn't need to know how the request will be carried out.

Command pattern to implement transactions
A Command pattern is also known as an action or transaction pattern. Let us consider a server that accepts and processes transactions delivered by clients via a TCP/IP socket connection. These transactions consist of a command, followed by zero or more arguments.

Developers might use a switch statement with a case for each command. Usage of Switch statements during coding is a sign of bad design during the design phase of an object-oriented project. Commands represent an object-oriented way to support transactions and can be used to solve this design problem.

In the client code of the program TestTransactionCommand.java, all the requests are encapsulated into the generic TransactionCommand object. The TransactionCommand constructor is created by the client and it is registered with the CommandManager. The queued requests can be executed at different times by calling the runCommands(), which gives us a lot of flexibility. It also gives us the ability to assemble commands into a composite command. I also have CommandArgument, CommandReceiver, and CommandManager classes and subclasses of TransactionCommand -- namely AddCommand and SubtractCommand. Following is a description of each of these classes:

  • CommandArgument is a helper class, which stores the arguments of the command. It can be rewritten to simplify the task of passing a large or variable number of arguments of any type.

  • CommandReceiver implements all the command-processing methods and is implemented as a Singleton pattern.

  • CommandManager is the invoker and is the Switch equivalent from the previous example. It stores the generic TransactionCommand object in its private myCommand variable. When runCommands( ) is invoked, it calls the execute( ) of the appropriate TransactionCommand object.

In Java, it is possible to look up the definition of a class given a string containing its name. In the execute ( ) operation of the TransactionCommand class, I compute the class name and dynamically link it into the running system -- that is, classes are loaded on the fly as required. I use the naming convention, command name concatenated by the string "Command" as the name of the transaction command subclass, so that it can be loaded dynamically.

Notice that the Class object returned by the newInstance( ) has to be cast to the appropriate type. This means the new class has to either implement an interface or subclass an existing class which is known to the program at compile time. In this case, since we implement the Command interface, this isn't a problem.

//TestTransactionCommand.java
import java.util.*;

final class CommandReceiver {

  private int[] c;
  private CommandArgument a;
  
     private CommandReceiver(){
       c = new int[2];
     }
   
     private static CommandReceiver cr = new CommandReceiver();
   
     public static CommandReceiver getHandle() {
	 return cr;
     }

     public void setCommandArgument(CommandArgument a) {
	 this.a = a;
     }

     public void methAdd() {
	 c = a.getArguments();
     	 System.out.println("The result is " + (c[0]+c[1]));
     }

     public void methSubtract() {
	 c = a.getArguments();
     	 System.out.println("The result is " + (c[0]-c[1]));
     }
}

 class CommandManager {

   private Command myCommand;

   public CommandManager(Command  myCommand) {
     this.myCommand  =  myCommand ;    
   }

   public void runCommands( ) {
     	    myCommand.execute();     
   }
   
 }

class TransactionCommand implements Command {

  private CommandReceiver commandreceiver;
  private Vector commandnamelist,commandargumentlist; 
  private String commandname;
  private CommandArgument commandargument;
  private Command command;
  
  public TransactionCommand () {
	this(null,null);
  }

  public TransactionCommand ( Vector  commandnamelist, Vector
commandargumentlist){
    this.commandnamelist = commandnamelist;
    this.commandargumentlist = commandargumentlist;
    commandreceiver =  CommandReceiver.getHandle();  
  }
 
  public void execute( ) {

    for (int i = 0; i < commandnamelist.size(); i++) {
     
      commandname = (String)(commandnamelist.get(i));
      commandargument = (CommandArgument)((commandargumentlist.get(i)));
      commandreceiver.setCommandArgument(commandargument);
      String classname = commandname + "Command";
     
         try {
           Class cls = Class.forName(classname);
           command = (Command) cls.newInstance();
         }
         catch (Throwable e) {   
                  System.err.println(e);
         }
      command.execute();
    } 
  }
}

 class AddCommand extends TransactionCommand {
   private CommandReceiver cr;

   public AddCommand () {
      cr = CommandReceiver.getHandle();  
   }  

   public void execute( ) {  
     cr.methAdd();  
   }   
 }

 class SubtractCommand extends TransactionCommand {
   private CommandReceiver cr;
    
   public SubtractCommand () {
      cr = CommandReceiver.getHandle();  
   }

   public void execute( ) {
     cr.methSubtract();
   }   
 }

 class CommandArgument {
   private int[] args;
   
   CommandArgument() {
     args = new int[2];
   }
   public int[] getArguments() {
	return args;
   }
   public void setArgument(int i1, int i2) {
         args[0] = i1; args[1] = i2;
   }
 }
     
 public class TestTransactionCommand {
   private  Vector clist,alist; 
   
   public TestTransactionCommand() {
	 clist = new Vector(); 
       alist = new Vector();
   }

   public void clearBuffer(Vector c, Vector a) {
	 clist.removeAll(c);
       alist.removeAll(a); 
   }

   public Vector getClist() {
	 return clist;
   }

   public Vector getAlist() {
	 return alist;
   }
    
    public static void main(String[] args) {
       CommandArgument ca,ca2;

     TestTransactionCommand t = new TestTransactionCommand();

     ca = new CommandArgument();
     ca.setArgument(2,8);
     Vector myclist = t.getClist();
     Vector myalist = t.getAlist();
     myclist.addElement("Add"); myalist.addElement(ca);

     TransactionCommand tc = new TransactionCommand(myclist,myalist);
     CommandManager cm = new CommandManager(tc);       
                    cm.runCommands();
     
     t.clearBuffer(myclist,myalist);
     ca2 = new CommandArgument();
     ca2.setArgument(5,7);
     myclist = t.getClist();
     myalist = t.getAlist();
     myclist.addElement("Subtract"); myalist.addElement(ca2);
     myclist.addElement("Add"); myalist.addElement(ca2);

     TransactionCommand tc2 = new TransactionCommand(myclist,myalist);        
     CommandManager cm2 = new CommandManager(tc2);       
                    cm2.runCommands();

   }
 }               

The command and its arguments are stored in a list and encapsulated into the generic TransactionCommand object. The generic TransactionCommand is registered with the CommandManager. The commands can be run at any time by invoking the runCommands() interface in the CommandManager class.

The client code doesn't depend on any of the concrete TransactionCommand subclasses, which means I have programmed to the interface and not the implementation. This gives us extensibility: to add a new command, we need to define a new TransactionCommand subclass and provide the implementation for the new command-processing method in the CommandReceiver class. That's all there is to it.

Conclusion
The Command pattern has the following advantages:

  1. A command decouples the object that invokes the operation from the one that knows how to perform it.

  2. A command is a first-class object. It can be manipulated and extended like any other object.

  3. Commands can be assembled into a composite command.

  4. It's easy to add new commands, because you don't have to change the existing classes.

When the sequence of commands executed is saved in a history list, you can iterate through the list to support undo and redo operations. You must have an unexecute() operation in the Command interface to implement this functionality. I will leave this as an exercise for the reader.

Some of this material was inspired by class notes from an object-oriented design class given by William E. Fairfield at the University of California Santa Cruz Extension.

Also this month in JavaWorld Nuts & Bolts: - How to write OpenCard card services for downloading Java - Lazy instantiation - Programming Java threads in the real world, Part 5 - Internationalize your software: Part 3 - Locating CORBA objects using Java IDL - Design with Runtime Class Information News & Views: - In search of the best Java book for beginners - News and New Product Briefs (11/14/98) - Sony and others support Sun's Jini - Sun wins bid to keep Java from splitting in two - Sun announces support for Java TV API for digital interactive TV - Mac Java news is delivered from the mothership at San Francisco's Macworld Expo - Jini, Sun's Magic Out of the Lamp - Jini, Sun's Magic Out of the Lamp - Update on Java development on and for the Mac - News and New Product Briefs (1/20/99) - Bluestone moves to marry Java and XML - Sun spills beans on future EJB releases - Discord surfaces in wake of Inprise restructuring - Letters to the Editor - Letters to the Editor

            

Resources

About the author
Bala Paranj is a corporate applications engineer at Mentor Graphics, Microtec Division. There he resolves complex technical problems of real-time operating systems, including test case generation, and research and evaluation of potential solutions. He has a Master of Science degree in Electrical Engineering from The Wichita State University, Wichita, Kansas. His research interests include applying design patterns to solve networking problems, concurrent programming, and implementation of the solution in Java. When he's not programming, he enjoys dancing, volleyball, chess, and spending time in nature. Reach Bala at bala.paranj@xxxxxxxxxxxxx.
What did you think of this article?

-Very worth reading
-Worth reading
-Not worth reading
-Too long
-Just right
-Too short
-Too technical
-Just right
-Not technical enough
Comments:
Name:
Email:
Company Name:     

[JavaWorld Main Page] [JavaWorld Search] [Nuts & Bolts] [News & Views] [Java Resources]

[(c) Copyright 1999 Web Publishing Inc., an IDG Communications company]

If you have problems with this magazine, contact webmaster@xxxxxxxxxxxxx
URL: http://www.javaworld.com/javaworld/javatips/jw-javatip68.html
Last modified: Friday, February 05, 1999