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.htmlTitle: Java Tip 68: Learn how to implement the Command pattern in Java - JavaWorld - February 1999
Java Tip 68: Learn how to implement the Command pattern in JavaAdd flexibility and extensibility to your programs with this object-oriented equivalent of the callbackSummary By Bala Paranj
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
Developers accustomed to using function pointers in another
language might be tempted to use the 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
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
Figure 1 below shows the
The concrete command,
The client instantiates the
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.
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
Command pattern example code
The example shows a
When the constructor for a
When the
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 --
Command pattern to implement transactions
Developers might use a switch statement with a case for each command.
Usage of
In the client code of the program
In Java, it is possible to look up the definition of a class given a
string containing its name. In the
Notice that the
//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
The client code doesn't depend on any of the concrete
Conclusion
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 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.
About the author
If you have problems with this magazine, contact
webmaster@xxxxxxxxxxxxx
|