Rice University - Comp 212 - Intermediate Programming

Spring 2008

Lecture #15 -  Introduction to Java Programing (part 1)

 

In Java Graphical User Interface (GUI) programming, we do not build GUI components from scratch. Instead we use GUI components provided to us by the JDK.  Java has two types of GUI applications: stand-alone GUI applications and applets.  We first study how to build stand-alone GUI applications (GUI app for short).

Every GUI app uses what is called a JFrame that comes with the JDK.  A JFrame is a window with borders, a title bar, and buttons for closing and maximizing or minimizing itself.  It also knows how to resize itself.  Every GUI app subclasses JFrame in some way to add more functionalities to the window frame.  One common task is to tell the system to terminate all "threads" of the GUI app when the user clicks on the exit (close) button of the main GUI window.  We encapsulate this task in an abstract frame class called AFrame described below.

0. Abstract Frame (AFrame.java)

Event

When the user interacts with a GUI component such as clicking on it or holding the mouse down on it and drag the mouse around, the Java Virtual Machine (JVM) fires appropriate "events" and delivers them to the GUI component.  It is up to the GUI component to respond to an event.  The abstract notion of events is encapsulated in an abstract class called AWTEvent provided by Java.  Specific concrete events are represented by appropriate concrete subclasses of AWTEvent.

For example, when the user clicks on the close button of a JFrame, the JVM fires an window event represented by the class WindowEvent and delivers it to the JFrame.  By default, the JFrame simply hides itself from the screen while everything else that was created and running before the JFrame disappears from the screen is still alive an running!  There is no way the user can redisplay the frame again.  In the case when the JFrame is the main window of a GUI app, we should terminate everything when this main frame is closed.  The best way to ensure this action is to "register" a special window event "listener" with the JFrame that will call the System class to terminate all threads related to the current program and exit the program.  The code looks something like this:

        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

Every time we write the constructor for a main frame of a GUI app, we invariably find ourselves writing the above lines of code to exit the program plus some additional application specific code to initialize the frame.  So, instead of "copy-and-paste" the above code ("opportunistic" re-use), we capture this invariant task in the constructor of an abstract class called AFrame and reuse the code by subclassing from it.  The application specific code to initialize the main frame  is relegated to the specific concrete subclass of AFrame and is represented by an abstract method called initialize().  The constructor for AFrame calls initialize(), which, at run-time, is the concrete initialization code of the concrete subclass of AFrame that is being instantiated., as shown below.


    public AFrame(String title) {
        // Always call the superclass's constructor:
        super(title);

        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent e) {
                System.exit(0);
            }
        });

        initialize();
    }

NOTE: Since Java 1.2, the call to instantiate a listener for the window closing event may be replaced by a call to

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

The following is a better version of AFrame.

/**
 * A better version of AFrame.
 * @author Nguyen Cong Vu
 * @since June 2004
 */
public class AFrame1 extends JFrame {
    public AFrame1(String title) {
        super(title);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        initialize();
        setVisible(true);
    }
    
    protected abstract void initialize();
}

Template Method Pattern: Expressing Invariant Behavior in terms of Variant Behaviors

The code for AFrame is an example of what is called the Template Method Pattern.  This design pattern is used to expresses an invariant and concrete behavior that consists of calls to one or more abstract methods.  An abstract method represents a variant behavior.  In other words, the template design pattern is a means to express an invariant behavior in terms of variant behaviors.  We shall see this pattern used again in expressing sorting algorithms.

Caveat

As stated earlier, the call to initialize() in the constructor of AFrame will only invoke the concrete initialize() method of the concrete descendant class of AFrame that is being instantiated.  Because initialize() is polymorphic, care must taken to ensure proper initialization of descendant classes that are more than one level deep in the inheritance hierarchy of AFrame.  Consider the following inheritance tree as illustrated by the following UML class diagram.  Click here to see the code.

In overriding the initialize() method of FrameA, a direct subclass of AFrame, we should not invoke the initialize() method of the superclass AFrame via the call

 super.initialize()

because super.initialize() is abstract.  

However, the initialize() method of any subclass of FrameA and below should make a call to the initialize() method of its direct superclass in order to ensure proper initialization.  For example, in the above diagram, 

Exercise: What is the chain of calls when we instantiate a FrameAC?

 

1. Simple JFrame (Frame0.java)

Frame0App.java represents one of the simplest GUI application in Java: it simply pops open an Frame0 object.  Frame0 is subclass of AFrame that does nothing in its concrete initialize() method.

2. JFrame with JButtons but no event handlers (Frame1.java)

To display other GUI components on a JFrame, we first need to get the content pane of the JFrame and then add the desired GUI components to this content pane.

If we want to arrange the added GUI components in certain ways, we need to add an appropriate "layout manager" to the JFrame.  The task of laying out the GUI component inside of a container GUI component is specified by an interface called LayoutManager.  In Frame1, we add the FlowLayout that arranges all GUI components in a linear fashion.  If we do not add any layout manager to the JFrame, it has no layout manager and does a very bad job of arranging GUI components inside of it.  As an exercise, comment out the code to add the FlowLayout to Frame1 and see what happens!

Strategy Pattern

JFrame is not responsible for arranging the GUI components that it contains.  Instead it delegates such a task to its layout manager , LayoutManager.  There are many concrete layout managers: FlowLayout, BorderLayout, GridLayout, etc. that arrange the internal components differently.  Their are said to be different strategies for layout.  The interaction between JFrame and its layout manager is said to follow the Strategy Pattern.

The strategy pattern is powerful and important design pattern.  It is based on that principle of delegation that we have been applying to model many of the problems so far.  It is a means to delineate the invariant behavior of the context (e.g. JFrame) and the variant behaviors of a union of algorithms to perform certain common abstract task (e.g. LayoutManager).  We shall see more applications of the strategy design pattern in future lessons.

At this point the only the buttons in the Frame1 example do not perform any task besides "blinking" when they are clicked upon.  The example in the next lecture will show how to associate an action to the click event.

Click here to download code samples.


©2004 Stephen Wong and Dung Nguyen