 | Level: Introductory Kelby ZorgdragerSun Microsystems Inc.
01 May 2001 The Java language utilizes patterns. The Java langauge, as an object-oriented programming language, was designed to empower developers to create solutions easily without worrying about the low level details of implementing the required data structures, stream handles, networking functionality, etc. The Java language blurs the line between a third generation language and a fourth generation language. In order to provide the empowerment claimed above, the Java language, at least much of its core, was designed and implemented using Design Patterns. The Java programming language includes implementations of patterns like the Iterator Pattern (found in the Java Collections APIs), the Delegation Pattern (found in the JavaBeans Event Model, or more explicitly the Delegation Event Model), the Observer Pattern (found in the MediaTracker and ImageObserver), the Factory Pattern (the URL class with its ability to handle many different types of URL connections), and others. However, instead of outlining where patterns exists, discussing why they are useful, and showing the evidence to prove this, I decided to write an article on how to use the Command Pattern when creating a reusable user interface solution. The intent is to provide an alternative thinking that may lead to a "new" event handling design.
The article covers a brief description of the Command Pattern and then uses three different examples to show why using the Command Pattern could prove useful in creating a design for a reusable user interface. In essence, reusability is obtained in a solution when the user interface components and the event handling logic are decoupled. However, many Java user interface solutions are implemented as a coupled solution where there is a tight association between the user interface components and the event handlers. Unfortunately, as you will see, this type of design and implementation limits the reuse of possibly both the user interface and the event handlers. As a note, some of the text in the article has been color coded, making the association between the point being made and the code representing the point.
Blue text represents some kind of coupling.
Green text represents some kind of decoupling. Bold text represents a major point presented in the source code.
One of the major goals (and promises) of OO development is that OO development yields reusable solutions. Though this is a goal, and maybe a good marketing pitch, not all OO solutions are reusable solutions. This could be a result of the system as a whole (or solution) not being designed with reuse in mind. It could also be due to the fact that the solution was not designed to "evolve" with time. Regardless of the reason(s), the outcome of a design without reuse in mind is a solution that "solves" a specific problem at a specific point in time. Design Patterns enable you to create reusable sub-systems of your entire solution in a relatively simple fashion. They may indeed allow you to create an entirely reusable solution! In many cases, the incorporation of Design Patterns in a solution "builds-in" reusability where the designer may not have considered it.
The Command Pattern is a relatively easy pattern to understand. It is basically an encapsulation of a request (method call on some object) as an object. The idea is that the object performing the request (the invoker) does not know in advance the details or semantics of the target object (object receiving the request) and uses the command to interact with the target.
For example, take object A and object B. Object A is represented by the class Wilma, and object B is represented by the class Fred. In common practices, for A to interact with B, A needs to "know" the interface (methods signatures describing the behaviors of an object) of B's class (Fred).
In other words, in order for Wilma to interact (and manipulate) Fred, Wilma needs to "know" and "understand" Fred's methods
. Without this insight, the two objects could not interact with one another. Or could they?
Let's say that Fred has a method called mowGrass. Building on the description above, for Wilma to tell Fred to mow the lawn, A would have to request (or invoke the method) mowGrass on object B.
This implies that Wilma has some preconceived notion and understanding about Fred. In the object-oriented world, especially one with dynamic bindings, this understanding may not exist at design time, or at compile time. One can further infer that if Fred decided to change mowGrass to takeKnap, Wilma would have to "know" about this change, and act accordingly (possibly through a maintenance update). Therefore, Wilma would have an intimate understanding of Fred.
The interactions between them and the solution they comprise would be tightly coupled, hindering reusability.
Revisiting the question asked above: Without this insight, the two objects could not interact with one another. Or could they? The answer is "Yes." One way to have two objects (possibly many objects) interact with each other without "knowing" the details and semantics of each class is to leverage the Command Pattern. As noted above, the Command Pattern represents some method invocation to be performed on some object as an object. The invoking object does not, and will not, know the method to perform on the target object.
So, let's go back to Wilma and Fred. If we follow the Command Pattern, Wilma would interact with Fred (A would interact with B) through the use of a command. The command would be represented as an object. In the description above, the request A was trying to make on B was to mow the lawn (by invoking the method mowGrass on B). Implementing this solution using the Command Pattern, the description would be more similar to this: A uses some object (C) which represents some method (mowGrass) to manipulate some other object (B). A does not have a preconceived notion of the target object (B), nor does A have a preconceived notion about the target method (mowGrass). The interactions between Wilma and Fred have been decoupled. This decoupling of the invoker and the target could quite possibly yield a more flexible and robust solution. In many cases, a decoupled system is a flexible system in which the pieces required for the system to work properly can be dynamically changed. A decoupled system also allows the components comprising the system to be leveraged in other, non-related systems, thus yielding reusability. As you will see below, the Command Pattern, leveraged in the context of event handling, enables us to create a reusable event handling mechanism.
Reusability, and design for reusability, should be considered in every single aspect of your solution's design. In fact, one common area where reusability is not normally considered is in the design of user interfaces. Despite this, there is a Design Pattern that addresses this: the Model View Controller (MVC) Pattern. If reusability is considered, and even designed for, it is probably in terms of the actual user interface. However, the UI is only part of the solution. The UI is useless without the event handling mechanisms. Reuse should be considered and designed for in event handling as well. Though reuse in terms of event handling may seem strange to you, it is quite possible and even practical.
Historically,
event handling in the Java language was tightly coupled with the user interface
. If you ever worked with JDK 1.0, and its hierarchical event delivery mechanism, events were delivered to and handled in the component (object). This means that if you wanted to handle events generated by a Button, you provided a subclass of java.awt.Button, overriding the implementation of "handleEvent". If at some later point in time you wanted to change the event handling "algorithm", you had to update the class, recompile it and redeploy it. Not a very robust system. Reuse was great as long as the new Button fit into your other solutions.
JDK 1.1 introduced the delegation event model. This was a drastic change to the event system of the Java programming language and to the development community who had become accustomed to the hierarchical model. Instead of adopting the reusable nature of the delegation event model, many developers continued to design and create solutions that were tightly coupled.
The introduction of the delegation event model could have been a grand opportunity for us to shift our thinking from coupled solutions to decoupled reusable solutions. (Kudos to those of you who have adopted this "new" thinking.)
The following code example represents a coupled event handling solution:
package icthus.articles;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class UserInterfaceOne implements ActionListener
{
private JFrame frame;
private JButton button;
private JTextArea area;
private int index = -1;
private String [] messages;
private UserInterfaceOne(String buttonLabel, String args[])
{
messages = args;
area = new JTextArea(5,25);
button = new JButton(buttonLabel);
button.addActionListener(this);
frame = new JFrame(getClass().getName());
frame.addWindowListener(new GenericWindowAdapter());
Container cont = frame.getContentPane();
JscrollPane scrollPane = new JScrollPane(area);
cont.add(scrollPane,BorderLayout.NORTH);
cont.add(button,BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
}
public void actionPerformed(ActionEvent ae)
{
//provide equality check to see if source was the
//button defined above.. useful only if we register
//this ActionListener with multiple sources
if(ae.getSource().equals(button))
{
index = ++index % messages.length;
area.setText(messages[index]);
}
}
public static void main(String args[])
{
if(args.length > 2)
{
String buttonLabel = args[0];
String [] messages = new String[args.length-1];
System.arraycopy(args,1,messages,0,messages.length);
UserInterfaceOne one = new UserInterfaceOne(buttonLabel, messages);
}
else
{
usage();
}
}
private static void usage()
{
System.err.println("java UserInterfaceOne button_label
message1 message2 ...");
System.exit(-1);
}
}
package icthus.articles;
import java.awt.*;
import java.awt.event.*;
public class GenericWindowAdapter extends WindowAdapter
{
public void windowClosing(WindowEvent we)
{
Object source = we.getSource();
if(source instanceof Frame)
{
((Frame)source).setVisible(false);
((Frame)source).dispose();
System.exit(0);
}
}
}
|
If you look at the class UserInterfaceOne, the class implements the ActionListener interface:
a coupled class hierarchy between the UI and the handler.
Close examination will also show you that the implementation of the interface is very specific to the solution. In fact, the implementation of actionPerformed directly interacts with the UserInterfaceOne object, changing the states of the object's members. If you wanted to use this event handler (the ActionListener) with an UI other than UserInterfaceOne, it would be nearly impossible. You could reuse the logic, following the copy-paste form of reusability. But you could not reuse the ActionListener, as it relies on the existence (in the context of a UserInterfaceOne object) of a JTextArea called "area", an integer called "index", and a String array called "messages". If all three of those exist, and were accessible to the ActionListener, then you could reuse the solution above. Practical? Unlikely.
The second class listed above is called GenericWindowAdapter. This class represents a WindowListener that handles windowClosing notifications. If you were to do a side by side comparison of this class with UserInterfaceOne, in terms of the event handling logic, you would notice one major difference.
In UserInterfaceOne, the ActionListener logic was tightly coupled to the implementation of the UserInterfaceOne class.
In GenericWindowAdapter, the logic was tightly coupled to the interface describing a Frame. Therefore, any object that is a Frame could use the GenericWindowAdapter to handle its events
. GenericWindowAdapter provides a reusable solution (though the exit logic is somewhat limiting).
Ideally, as mentioned above, the change in the event model would have implicitly brought about a change in solution design. The following example represents a redesign targeting reuse of the ActionListener:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class UserInterfaceTwo
{
private JFrame frame;
private JButton button;
private JTextArea area;
private int index = -1;
private String [] messages;
private UserInterfaceTwo(String buttonLabel, String args[])
{
messages = args;
area = new JTextArea(5,25);
button = new JButton(buttonLabel);
button.addActionListener(new SemiGenericActionListener(this));
frame = new JFrame(getClass().getName());
frame.addWindowListener(new GenericWindowAdapter());
Container cont = frame.getContentPane();
JscrollPane scrollPane = new JScrollPane(area);
cont.add(scrollPane,BorderLayout.NORTH);
cont.add(button,BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
}
void setMessage(Object source)
{
if(source.equals(button))
{
index = ++index % messages.length;
area.setText(messages[index]);
}
}
public static void main(String args[])
{
if(args.length > 2)
{
String buttonLabel = args[0];
String [] messages = new String[args.length-1];
System.arraycopy(args,1,messages,0,messages.length);
UserInterfaceTwo two = new UserInterfaceTwo(buttonLabel, messages);
}
else
usage();
}
private static void usage()
{
System.err.println("java UserInterfaceTwo button_label
message1 message2 ...");
System.exit(-1);
}
}
import java.awt.event.*;
public class SemiGenericActionListener implements ActionListener
{
private UserInterfaceTwo two;
SemiGenericActionListener(UserInterfaceTwo too)
{
two = too;
}
public void actionPerformed(ActionEvent ae)
{
two.setMessage(ae.getSource());
}
}
|
This solution contains more reusable components than that of the first example, primarily due to the somewhat decoupled event handler (ActionListener).
The UI is represented in a separate class from the event handling mechanism used to manipulate the UI. This basic separation already places us one step closer to a reusable solution. If it is desirable to reuse the SemiGenericActionListener in another solution, we could simply create an instance of the class, and register the instance with a source, which generates ActionEvents.
However, the solution above still relies on some previous knowledge. If you look closely at the actionPerformed method (of SemiGenericActionListener), the event handling logic invokes a well known method (setMessage) on a well known type (UserInterfaceTwo). This means that in order to reuse SemiGenericActionListener in another solution, the target type would either have to be a UserInterfaceTwo or a subclass. So just like the first example, the reusability comes with a price. In this case the price is that the source of the request has to be a well known type supporting a well known method. Decoupled? Perhaps. Reusable? Perhaps.
One variation of the previous example is to create an interface that declares a well known method (or methods). For example, the interface would be Messenger, with the method setMessage. The UserInterfaceTwo class would implement this interface.
The event handler could then invoke setMessage on the target based on its interface declaration rather than its implementation declaration. This design broadens our decoupling even further as it follows the common OO rule "Program to the interface, not the implementation". In this solution, we could have a third class, UserInterfaceNone, implement the same interface. UserInterfaceNone could then register a SemiGenericActionListener with one of its members. The SemiGenericActionListener could perform the setMessage method on the UserInterfaceNone object.
This solution is better, but still relies on some kind of knowledge. At least the knowledge is at the interface level, rather than the class level, yielding a more flexible and extensible system.
The best solution would be one where the event handler does not know about (in advance - meaning design time or compile time) the target's interface nor the appropriate method to call on the target. This solution would be a true representation of a decoupled solution. This solution is one in which the target could change (in terms of type and functionality), the logic used to handle the event could change (in terms of the request object), and the event handler could be used and reused in many systems. If you remember our definition from above, it would also represent a solution that leverages the Command Pattern. This leads us to the last example.
In order to create a decoupled event handling solution, following the Command Pattern, we need to decouple the event handler from the UI. Similar to the second example above, we could create a separate class for the UI and the event handler, providing a decoupling at the class hierarchy level. We also need to decouple the event handling logic from the UI or make it generic instead of specific. Ideally we could create an event handler that makes a request on some object using a command. The logic of the event handler would invoke the command. Realize that the logic would not "know" the details of the command, rather just how to perform the invocation. We need to create three different things for this solution to work: the UI; the command (an object); and the event handler. Lucky for us we can modify the UI from the previous two examples, making it specific to this example. We can also modify the event handler, making it more generic. However, we will have to find a way to represent a command as an object.
Fortunately, in JDK 1.1, reflection was introduced into the Java language. Reflection, if you are not familiar with it, enables the developer to dynamically introspect a class. Introspection allows you to discover the fields, methods, constructors, and signature information of any class. The fields are represented as java.lang.reflect.Field objects. The methods are represented as java.lang.reflect.Method objects. And the constructors are represented as java.lang.reflect.Constructor objects. All of this information can be obtained from a class using the java.lang.Class class. Looks like we found a way to represent a command as an object. If you did not put one and one together to get two; we can use reflection to create a Method object which will represent our command. This article is not on Reflection, so check out the JDK APIs and Documentation for more information.
Now that we know how to represent the command as an object (fulfilling the requirement of the Command Pattern definition), we need to make the appropriate changes to the classes used in the previous two examples.
Remember the goal is to create a completely reusable ActionListener
. To do this, we need to change the event handling logic to invoke the command instead of some specific method. If you look at the APIs for the java.lang.reflect.Method class, you will find that there is an invoke method used to invoke the method on a specified object with the specified arguments. In order to make our event handler generic and reusable, we can't hard code the target or the arguments used in the method invocation. This information needs to be set dynamically at run-time. Therefore, we have included a constructor to our ActionListener that takes the required information in order for the event handling to work. The GenericActionListener requires the command (represented as a Method), the target (object on which to invoke the command), and the argument list (method parameter list represented as an Object[]). Once the GenericActionListener has been initialized, it is ready to receive notification from the JVM when an event is generated. The event handling achieves the same result as in the previous examples: a new message is displayed in the JTextArea.
However, the result is achieved through the invocation of the command associated with the GenericActionListener
. The following code example represents the GenericActionListener. Notice that the name has been changed from SemiGenericActionListener to GenericActionListener.
class GenericActionListener implements ActionListener
{
private Method command;
private Object [] methodArguments;
private Object target;
GenericActionListener(Method method, Object target, Object [] args)
{
command = method;
this.target = target;
methodArguments = args;
}
public void actionPerformed(ActionEvent ae)
{
try{
command.invoke(target, methodArguments);
}
catch(Exception e){
e.printStackTrace(System.out);
}
}
}
|
As you can see from the example above, the actionPerformed method does one thing: it invokes the command.
The GenericActionListener, in particular the actionPerformed method, does not "know" about the method signature, the target's interface, or any of the details required to invoke the method (setMessage). This information has been decoupled from our event handler. So we have achieved the goal of creating a generic, reusable event handler.
In order for our solution to work, we also need to update the UI. The following code example shows you how to create the Method object, instantiate the GenericActionListener, and provide the association between the UI and the event handler at run-time. Using Reflection (the Class class) we obtain the Method object, from the class UserInterfaceThree, representing the method setMessage. We then pass the Method object, the target of the method invocation, and the method parameters to the GenericActionListener constructor. Finally we register the ActionListener with the JButton.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.lang.reflect.*;
public class UserInterfaceThree
{
private JFrame frame;
private JButton button;
private JTextArea area;
private int index = -1;
private String [] messages;
private UserInterfaceThree(String buttonLabel, String args[])
{
messages = args;
area = new JTextArea(5,25);
button = new JButton(buttonLabel);
//setup required information to use GenericActionListener
String methodName = "setMessage";
Class [] paramTypes = { java.lang.Object.class };
Object [] methodArgs = { button };
Class clazz = this.getClass();
try{
Method method = clazz.getMethod(methodName, paramTypes);
button.addActionListener(new
GenericActionListener(method, this, methodArgs));
}
catch(Exception e){
System.out.println("Could not find the method: "+methodName+"\nExiting...");
System.exit(-1);
}
frame = new JFrame(getClass().getName());
frame.addWindowListener(new GenericWindowAdapter());
Container cont = frame.getContentPane();
JScrollPane scrollPane = new JScrollPane(area);
cont.add(scrollPane,BorderLayout.NORTH);
cont.add(button,BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
}
public void setMessage(Object source)
{
if(source.equals(button))
{
index = ++index % messages.length;
area.setText(messages[index]);
}
}
public static void main(String args[])
{
if(args.length > 2)
{
String buttonLabel = args[0];
String [] messages = new String[args.length-1];
System.arraycopy(args,1,messages,0,messages.length);
UserInterfaceThree three =
new UserInterfaceThree(buttonLabel, messages);
}
else
usage();
}
private static void usage()
{
System.err.println("java UserInterfaceThree button_label
message1 message2 ...");
System.exit(-1);
}
}
|
The exception handling has been simplified, allowing you to focus more on how the Command Pattern was implemented.
The solution provides an example of how to implement the Command Pattern. It also provides an example of how to create a reusable event handler.
Following a user interface design similar to the one presented above, where the event handling is decoupled from the user interface component, enables you to reuse the event handler in other solutions. For example, if we created a class called UserInterfaceFour, containing two JButtons, one to set a message, and one to delete a message, we could use the same event handling logic to handle the events. As a result of pressing the "set message" button, the event handler would invoke setMessage(). Likewise, pressing the "delete message" button would result in the event handler invoking the deleteMessage() method. Realize, in this solution, the event handling logic is completely generic. The logic only knows how to invoke the command represented by the Method object. Therefore, we would have to create two Method objects, one to represent the command setMessage, and one to represent the command deleteMessage. As in UserInterfaceThree, we would pass the commands to the constructor of the GenericActionListener class and perform the event handler "registration" with the user interface component. The event handler, when notified by the AWTThread, would invoke the command and either set a message or delete a message.
Decoupling the event handling logic from the event handler is one design pattern that will bring you closer to a reusable user interface solution. There are many different variations of how to use the command pattern to create a reusable user interface. It may be interesting (and hopefully advantageous) to decouple the solution even further by creating a third class that handles the actual event logic. This would allow you to decouple the method "setMessage" from the UserInterfaceThree class. Regardless of the incarnation, the usage of the command pattern is useful in the creation of a reusable user interface.
In summation, we used Reflection to create a Method object representing the command. We then passed the command to the event handler, which invoked the command on the target after receiving event notification. Designing for reuse is an important aspect of any object-oriented solution. Design patterns enable you to incorporate reuse into your design and your implementation, yielding a more flexible and extensible solution.
Resources -
Design Patterns: Elements of Reusable Object-Oriented Software; Gamma, Helm, Johnson, and Vlissides: Addison-Wesley Publishing, Reading, MA; 1995
-
Java Swing; Eckstein, Loy, and Wood: O'Reilly and Associates, Sebastopol,CA; 1998
-
The Complete Java 2 Certification Study Guide: Roberts, Heller, and Ernest: Sybex Inc., Alameda, CA: 1999
-
Java Developer Connection
-
The Source for Java Technology
About the author  | 
|  | Kelby Zorgdrager is a Sr. Software Engineer at Sun Microsystems Inc. Prior to working as a Software Engineer, Kelby was a Sr. Java Instructor for Sun Educational Services where he provided training and consulting services to over 2500 students. Kelby has presented at major Java Trade Shows including JavaOne, Java Business Expo, and COMDEX. Kelby has
worked with, and developed in, the Java language since January of 1996.
|
Rate this page
|  |