Java Tip: Implement Command Mode
Overview Sometimes it is necessary to send requests to an object, but do not know any information of "requested" or "request recipient". In a process-oriented programming language, such communication is done by a callback function: register this function in a certain place, then call it later. In the object-oriented program, the command (command) is equivalent to the callback function, which encapsulates the callback function. This article demonstrates how to implement Command mode in Java.
-------------------------------------------------- -------------------------
The design pattern can not only accelerate the design progress for object-oriented engineering, but also improve the output of the development team and the quality of the software. Commmad mode is an object behavioral mode that fully decoupled for the sender (Sender) and Receiver. ("Sender" is the object of request operation, "Receiver" is an object that receives requests and executes an operation. With "decoupling", the sender knows nothing about the receiver's interface.) Here, "Request" (REQUEST) This term refers to the command to be executed. The Command mode also allows us to change the "when" and "how" complete the request. Therefore, Command mode provides us with flexibility and scalability.
In the programming language like C, the function pointer is often used to eliminate the huge Switch statement. (See "Java TIP 30: Polymorphism and Java" Get a more detailed description) Java does not have a function pointer, so we can use Command mode to implement the callback function. In the first code sample sample TestCommand.java below, you will actually see how it is implemented.
Some developers have become accustomed to using function pointers in other languages, so they often ban temptation, want to use the reflection API's Method object in the same way. For example, in the "Java Reflection" article, Paul Tremblett describes how to use the Reflection instead of the SWITCH statement to implement transaction (see "Related Resources" get the link of TreMblett's article and Sun's Reflection tutorial URL link). I won't move to this temptation. As Sun is pointed out: in the case where the tools that are more closely close to the Java programming language meet the requirements, the reflective API is generally not advocated. If you don't use the Method object, the program will easier debugging and maintenance. So, you should define an interface and implement it in the class to perform the required operation.
Therefore, I suggest that the Command mode can be used and combined with the dynamic load and binding mechanism of Java to implement function pointers. (For details on the dynamic loading and binding mechanism of Java, see Gosling and Henry McGilton's "The Java Language Environment - A White Paper" is listed in "Related Resources".)
Follow the above recommendations, we can use the Command mode to use the polymorphism it provides to eliminate the large SWITCH statement to design scalable systems. We can also use Java unique dynamic load and binding mechanisms to construct dynamic, and can dynamically expand systems. This is described in this second code example TestTransactionCommand.java.
The Command mode makes the request itself an object. This object can be stored in the same way as other objects. The key to this mode is a Command interface: it declares an interface for performing operations. Under the simplest form, this interface contains an abstract execute operation. Each specific Command class stores the recipient as an instance variable to specify a pair of "recipients" and "behavior". It provides different implementations for the execute () method for request calls. The recipient knows how to perform a request. Figure 1 shows the aggregation of the Switch - a Command object. There are two operations in its interface. Switch is called "invoker" because it calls the Execute operation in the Command interface.
Specific Command, such as LightonCommand, implement the Execute operation of the Command interface. It knows to call the operation of the appropriate recipient object. In this case it acts as an adapter. Through the term "adapter", I want to explain that the specific Command object is a simple connector that connects "callers" and "recipients" with different interfaces.
Customer instantiate caller, recipients, and specific Command objects.
Figure 2 is a timing chart, which represents the interaction between the objects. It illustrates how Command is decoupled for the caller and recipient (and the request it execute). The customer uses the appropriate recipient as a parameter of the constructor to create a specific Command. Then it saves Command in the caller. The caller calls the specific Command, and the latter knows how to complete the desired ACTION () operation.
Customers (main program in the code) Create a specific Command object and set its recipient. As a caller object, Switch saves the specific Command object. The caller sends a request by calling Execute to the Command object. The specific Command object operates the recipient to complete the operation request.
The most critical thinking here is that the specific Command is registered with the invigible, the caller is called, and the command is executed on the recipient.
Command Mode Sample Code Let's take a simple example that implements a callback mechanism through the Command mode.
Take Fan (Fan) and Light as an Example. Our goal is to design a Switch that can "open" and "off" for an object. FAN and Light have different interfaces, which means that Switch must be independent of the recipient interface, or that it doesn't know the receiver's interface. In order to solve this problem, each Switch requires a suitable Command as a parameter. Obviously, connect to Light Switch and Switch connecting to FAN has different Command. Therefore, the Command class must be abstract or an interface.
When the Switch constructor is called, it is used as a parameter in a set of suitable Command. Command saved as a private variable of Switch.
When flipup () and flipdown () are invoked, they simply make the appropriate Command for Execute () operations. Switch will not know anything about calling execute ().
Testcommand.javaclass 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 imports Command {Private Light MYLIGHT; Public Light OnCommand (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.javapublic interface Command { Public abstract void execute ();
In the sample code above, the Command mode completely separates the "Switch" and "Light and Fan know how to perform operation" (Light and Fan). This brings a lot of flexibility: the object sent the request only needs to know how to send; it doesn't have to know how to complete the request.
Command mode implementation TransactionCommand mode is also known as an Action mode or Transaction mode. Assume that there is a server that receives and handles the transaction sent through the TCP / IP Socket connection. These transaction contain one command, followed by zero or more parameters.
Some designers may use the Switch statement, each Command corresponds to a CASE. In an object-oriented engineering design, if you use the Switch statement in your code, this is often a bad design. The Command mode shows object-oriented ways to support Transaction, which can be used to solve such design problems.
In the customer code of the TestTransactionCommand.java program, all requests are encapsulated in a universal TransactionCommand object. TransactionCommand objects are created by customers and registered with CommandManager. Waiting requests can be implemented by calling RunCommands () in different periods, which has brought great flexibility. And we can also assemble multiple Command into a composite Command. There are ComMandargument, CommandReceiver, CommandManager, as as well as subclass of TransactionCommand, SubmmandManager - ADDCommand and SubtractCommand. Here is an introduction to these classes. · Commandargument is a Helper class that saves the parameters of the command. If it is a large (or variable number) of any type of parameters, it can be rewritten to simplify the passage of the parameters.
· CommandReceiver implements all command processing methods (Command-Processing Method), which is implemented in Singleton mode.
· CommandManager is a caller, and the Switch in the previous example is quite. It saves General TransactionCommand objects in its private MyCommand variable. When RunCommands () is called, it calls the execute () of the appropriate TransactionCommand object.
In Java, you can look for the definition of classes based on a string containing class name. In the Execute () operation of the TransactionCommand class, I first calculate the class name, then link it to the running system - that is, the class is instantly loaded as needed. The name of the name used here is that after the command name is connected to a "command" string as the Transaction Command subclass, so that it can be dynamically loaded.
Note that the Class object returned by NewInstance () must be converted to the appropriate type. This means that the new class either implements an interface or inherits an existing class known to the program in the compile period. In this example, we implement the Command interface, so there is no problem. File: //testtransactioncommand.javaimport java.util. *;
Final Class CommandReceiver {Private Int [] C;
PRIVATE COMMANDARGUMENT A;
Private commandRecEcess () {
C = new int [2];
Private static commandreceiver cr = new commandReceiver (); public static commandreceiver getHandle () {Return Cr;
} Public void setcommandargument (commandAndargument a) {this.a = a;
Public void methadd () {c = a.getarguments ();
System.out.Println ("The Result IS" (C [0] C [1]));
} Public void method () {c = a.getarguments ();
System.out.println ("THE RESULT IS" (C [0] -C [1]));
}} Class CommandManager {Private CommandMANager; Public CommandManager (Command MyCommand) {this.mycommand = MyCommand;
} Public void ruincommands () {
MyCommand.execute ();
}
Class TransactionCommand IMPLEments CommandReceiver;
Private Vector CommandNameList, CommandargumentList;
Private string commandname;
Private commandargument Commandargument;
Public TransactionCommand; (NULL, NULL);
} Public transactionCommand (Vector CommandNameList, VectorCommandargumentList) {
This.commandnamelist = CommandNameList;
This.commandargumentListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListListList;
CommandReceiver = CommandReceiver.getHandle ();
} Public void execute () {for (int i = 0; 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 CommandRecEver Cr; Public Addcommand () { Cr = commandRecEiver.getHandle (); PUBLIC VOID EXECUTE () { Cr.Methadd (); } } Class SubtractCommand Extends TransactionCommand { Private CommandRecEver Cr; Public SubtractCommand () { Cr = commandRecEiver.getHandle (); PUBLIC VOID EXECUTE () { Cr.methsubtract (); } } Class commandAndargument { Private int [] args; commandargument () { Args = new int tent [2]; } Public int [] getarguments () {return args;} Public void setargument (int I1, int 2) { Args [0] = I1; Args [1] = I2; } PUBLIC CLASS TestTTRANSACTIONCOMMAND { 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 ();}} Commands and their parameters are saved in the list and are encapsulated into a general transportCommand object. General TransactionCommand is registered with CommandManager. At any time, the command can be executed by calling the RunCommands () interface in the CommandManager class. The customer code does not depend on any specific transactionCommand subclass, that is, my design is for the interface instead of implementation. This brings flexibility: To add a new command, you only need to define a new TransactionCommand subclass and provide a new command processing method in the CommandReceiver class. This is only. Conclusion The Command mode has the following advantages: 1. Command deposes the objects of "Operation Request" and "know how to perform operation" (ie, decoupling). 2. Command is a great object. It can be used and inherited like any other object. 3. Multiple Command can be assembled into a composite Command. 4. It is easy to add new Command because you don't need to modify an existing class. If the executed command sequence is saved in a historical list, you can traverse this list to provide UNDO and REDO operations. To achieve this, there must be a unExecute () operation in the Command interface. This exercise leaves the reader to complete it. -------------------------------------------------- ------------------------- Resource · Design Patterns by Gamma, Helm, Johnson, Vlissides, Addison-Wesley, 1994, ISBN 0-201-63361-2 http://www.bookbuyer.com/cgi-bin/gettitle.cgi?bn=0201633612 Dr. Dobb's Journal, Java Reflection: NOT JUST for Tool Developers, "by Paul TreMblett http://www.ddj.com/articles/9801c.htm Sun's reflection page http://java.sun.com/docs/books/tutorial/reflect/index.html · "The Java Language Environment - A White Paper" (May 1996), by James Gosling and Henry McGilton covers details about Java's dynamic loading and binding mechanism http://java.sun.com/docs/white/langenv/ For more ON Taking Advantage of Java's Unique Feature Of Dynamic Loading and Binding Mechanism To Build a Dynamic And Dynamically-Extensible System, See http://java.sun.com/docs/white/langenv/