Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Submission Title Actions and Encapsulation of Behavior Contact Information Richard Rasala [email protected] Viera K. Proulx [email protected] College of Computer & Information Science Northeastern University Boston MA 02115 Problem Statement In Java, there are several mechanisms to deal with events and the actions they should trigger. For GUI components, Java defines the ActionEvent class and an interface ActionListener for objects that should respond to such events. The ActionListener interface requires just one method: public void actionPerformed(ActionEvent e) At that time, we said that the Paint button was defined using an action. Here is the definition of that action: protected Action paint = new SimpleAction("Paint") { public void perform() { paint(); } }; As you can see, the paint action calls the paint method which we sketch below: protected void paint() { Thus, ActionListener represents pure encapsulation of behavior. Java also defines an interface Action that extends ActionListener. The Action interface requires that the object maintain a property list for auxiliary settings and that it be capable of being enabled or disabled. Java provides a base implementation of Action in the AbstractAction class that leaves actionPerformed undefined. // define the circle using radius, x, y ... When Java deals with GUI components, it normally provides methods that allow programmers to add ActionListeners to react to events. However, in the constuction of a component such as a JButton, Java requires an Action because the auxiliary settings are used to define the button label or icon. g.fill(circle); Although this is a nice design, there are problems: In dealing with button clicks and similar events, one almost never needs to deal with the actual ActionEvent. Instead one just wants to define the response behavior in a simple fashion. There is no provision for response behavior to be done in a separate thread. This is often vital since performing intense algorithms in the thread that listens to the GUI will cause the GUI to appear to die. Java has many event classes. Often one wants to do the same response to different events but this is difficult due to the multiplicity of event classes. Solution Overview In this section, we will describe various classes that solve some of the problems mentioned above. SimpleAction To made it easy for faculty and students to define Action objects that do not need the ActionEvent, JPT defines the SimpleAction class that extends the Java AbstractAction class. To complete the definition of a SimpleAction, one must define: public void perform() The actionPerformed method is a final method that simply calls perform. In fact, for flexibility, SimpleAction also implements the interfaces ChangeListener and PropertyChangeListener by the same technique. Example: In the submission on GUI Composition, we illustrated aspects of GUI building with the Circle Sample example. // get the graphics context of the window Graphics2D g = window.getBufferGraphics(); // set anti-aliasing on ... // set the paint color and fill the circle g.setPaint(colorView.getColor()); // repaint the window window.repaint(); } Now let us explain why this works and how we teach it. The paint action and paint method are permitted to have the same name because data and methods are in two different namespaces. Given this, we adopt a pedagogical convention that an action will be defined using a method of the same name. This makes the action definition short and focuses the behavior definition in a method where students would normally expect to see behavior. The action definition uses anonymous inner classes but we would never use such scary words in teaching. Instead, we explain that the braces { ... } provide syntax to extend the definition of an object by supplying missing methods or by permitting existing methods to be overridden. To students, this makes sense. The perform method is missing and the braces provide a simple way to supply it. Finally, the data parameter "Paint" is attached to the NAME property of the Action and therefore becomes the button label in the GUI. We use the fact that TablePanel automatically turns actions into buttons. In summary, the students see that we get a button from an action whose behavior is defined in a corresponding method. is used throughout JPT GUI building. Students get used to the idea of encapsulating behavior in objects by working with this easy concept. This prepares them for more sophisticated encapsulations of algorithms as strategy objects. SimpleAction ThreadedAction The class ThreadedAction is a wrapper class that encapsulates an action for the purpose of performing that action in a separate daemon thread. Here is how the actionPerformed method is defined: public void actionPerformed(ActionEvent event) { // if there is no action then exit if (action == null) return; // define the thread final ActionEvent eventcopy final Action = event; actioncopy = action; Thread thread = new Thread() { public void run() { actioncopy.actionPerformed(eventcopy); } }; The abstract class MouseAction extends AbstractAction and encapsulates an action that is performed as a result of a Java MouseEvent. To instantiate a MouseAction object, one must define the method: public void mouseActionPerformed(MouseEvent mevt) A MouseActionAdapter object is designed to listen to a specific component for MouseEvents. The MouseActionAdapter can add, remove, set, or get actions to take place if any of the seven standard MouseEvents takes place within the component. To illustrate what is possible, we list the seven add methods that add a single Action: // run the thread public void addMouseClickedAction(Action a) thread.setDaemon(true); public void addMouseEnteredAction(Action a) thread.start(); public void addMouseExitedAction(Action a) } public void addMousePressedAction(Action a) The essence of this definition is that the run method of the new thread executes the original action on the original event. The need for copying the object references into final variables is just annoying Java mumbo jumbo. The usage of ThreadedAction by students is trivial. If a student has defined an action object whose algorithm will be time consuming then instead of inserting the action directly into a GUI the student is instructed to insert the wrapped action: new ThreadedAction(action) In particular, the student does not need to know about threads other than to know that they create separate execution branches and prevent an intense algorithm from hogging all of the CPU. Normally a student would not see the thread code given above until they had much more experience and wanted to learn how the ThreadedAction class works. The code is included here to help the Task Force know what ThreadedAction is doing. Aside: In the Java Power Framework, all automatic buttons are defined by actions wrapped using ThreadedAction. Thus, at the top level, the JPF application is multithreaded. public void addMouseReleasedAction(Action a) public void addMouseDraggedAction(Action a) public void addMouseMovedAction(Action a) In practice, the action that is passed to one of these methods is either a MouseAction or a SimpleAction. A SimpleAction will perform the same task independent of the MouseEvent data. The class MouseActionEvent is an adapter event class that extends ActionEvent and represents an action triggered by a MouseEvent. Normally, the use of MouseActionEvent is internal to JPT. Example: Let us show how to add an action that will track the mouse position by printing its coordinates into 2 text fields called xTFV and yTFV. The tracking action is defined to call a method as is our standard paradigm. MouseAction trackMouse = new MouseAction() { public void mouseActionPerformed(MouseEvent mevt) { trackMouse(mevt); } }; The corresponding method is defined as follows. protected void trackMouse(MouseEvent mevt) { xTFV.setViewState(mevt.getX() + ""); ActionSequence An ActionSequence object encapsulates an ordered list of Action objects and is itself an Action. The actionPerfomed method for the sequence executes the actionPerformed method of each action in turn. Thus, ActionSequence implements the composite pattern for Action objects and provides a mechanism for adding or removing actions incrementally. yTFV.setViewState(mevt.getY() + ""); } Finally, if adapter is the name of the MouseActionAdapter for the component, then the action is installed into adapter as follows. adapter.addMouseMovedAction(trackMouse); For many additional examples of installing mouse behavior, see Chapter 1 of the JPT Book on the JPT web site. Action Adapters The purpose of the classes in this category is to adapt event handlers to fit into the Action paradigm and, in particular, to be able to use ActionSequence for storage of one or more of the handlers. Normally, these handlers form groups of three classes: nameAction nameActionAdapter nameActionEvent The name signifies what is being adapted. Since there are nine groups of three adapters that are all quite similar, we will describe only one group, namely, Mouse adapters. The other groups that we have found particularly useful are Key adapters and Window adapters. The remaining adapters are used only in special situations. Experience with the Solution The use of actions and action adapters follows a strict paradigm that is easy for students to learn and use in the context of building GUIs. More generally, students become comfortable with the idea of encapsulation of behavior in objects. This prepares them for learning the strategy pattern and for concepts in higher order languages. API Documentation & Related Materials The main JPT site to access documentation, code, and the jpt.jar: http://www.ccs.neu.edu/jpt/