SummaryBy Bala Paranj
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)
esign
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
Switch
s 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:
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.
ACKNOWLEDGEMENT
The author wishes to thank Dr. Patrick Worfolk for his detailed comments on earlier versions of this article.
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.
Feedback :
Mail to
mailbox of bparanj