TOC PREV NEXT INDEX

Put your logo here!

The Essence of Object Oriented Programming with Java and UML


Chapter 5

Object-Oriented Graphical User Interfaces with Swing

Up to this chapter, we've concentrated on object-oriented concepts. By now, you should have a good grasp of the basic object-oriented concepts. You should be starting to think objects when it comes to programming in Java. This chapter will cover Graphical User Interfaces, and the Java Swing library with the same object-oriented perspective.

Graphical User Interfaces, or GUIs, are how users interact with programs. Almost any program will have a GUI part to it. Programming with a GUI library, or toolkit as they are commonly called, can be a daunting task. There are dozens of methods to use, user actions to respond to, and conventions to follow when designing the user interface.

However, looking at a GUI toolkit with object-oriented eyes can make the whole process much easier. By getting a fundamental understanding of the relationships between the objects of a GUI toolkit (Java's Swing, in this case), and what goals the GUI objects need to accomplish, the whole task of building GUIs with clean and simple code becomes possible.

In this chapter, we will cover the essence of GUIs. First, we will discuss GUIs in general terms to get the big picture of what they need to do. Next, we will take a big picture, object-oriented look at Swing. Taken in this perspective, Swing becomes much less complex and easier to understand. We will show how to handle user events with Swing in some simple examples. Finally, we will discuss the Model-View-Controller (MVC) design. This design is commonly used to structure object-oriented GUI programs. We will develop a small MVC framework, and build a simple Swing GUI based MVC application.

framework A collection of classes designed to provide a set of services for a particular problem area. The designers of a framework should be experts in a problem domain, and thus provide a higher level design that makes it easier for an application using the framework to build an application for that problem domain.

Graphical User Interfaces

GUIs are how most applications interact with the user. There is a lot in common with the interfaces used by most applications. This is a good thing, because users have come to expect a certain look-and-feel from their applications. While the visual details may differ, most applications have windows that show views of the data being manipulated, and use menus, tool bars, and dialogs to provide interaction with the user. The user interacts with the program using a pointing device, usually a mouse, and the keyboard.

A Typical Application

The MovieCat application shown in Figure 5-1 is fairly representative of many Java applications. This application is actually the example we develop in Chapter 6, so we are using it here to demonstrate some common GUI attributes.

Figure 5-1.
Typical GUI - MovieCat App

This application has an outer frame with a title bar at the top. In this case, the title bar has some special icons in addition to the title. The exact look of the title bar and surrounding frame will depend on the host environment - Windows vs. Motif, for example. This example shows the Java metal look and feel.

Inside the outer frame there is a menu bar, a tool bar, and a split pane that shows a list on the left and information on the right. The menu bar may be the most standard component, and consists of pull-down menus. The tool bar usually has buttons and other components that are most often used in interacting with the program. The area below is the main window or canvas to the program. This is where the information is usually displayed. In this case, it is a split window with two views into the data: the left shows all movies, while the right shows the specific movie. In this example, both view panes have additional controls - a list and some command buttons. In other applications, these panes could be drawing canvases, and the interaction could be done using mouse motion and clicks. But many applications follow this general pattern: menu bar, tool bar, view area.

Dialog Boxes

Dialog boxes are another common way to implement user interfaces. They usually pop up in response to some command in the main application window. These can be file choosers, color choosers, or specialized tools. There are two kinds of dialogs: modal and modeless dialogs. In a modal dialog, interaction with any other window or dialog is locked out until the user interacts with the dialog. In a modeless dialog, the user can continue to interact with other parts of the application while the dialog remains displayed. Modal dialogs will go away once the user enters a command. Modeless dialogs may remain visible and available even while the user interacts with the main program window.

Most GUI toolkits provide a standard set of control components. The exact look and feel of these can vary, but the most common controls include:

Toolkits that supports these components usually provide the capability to layout and group controls within a dialog or other display frame. The dialog in Figure 5-2 is from the MovieCat application. It uses label, text input, combo box, and button controls.

Figure 5-2.
Dialog Box - MovieCat Edit Dialog

Events

When a user clicks on a button, selects an item from a list, or performs some other command, the program must respond appropriately. The structure of the code for user command processing in GUI applications is based on events. Interaction with an application from the user's viewpoint consists of a series of mouse movements and clicks, and text and command input through the keyboard. From the programmer's viewpoint, each of these is an event. The important thing about an event is that it can occur at any time, and the program cannot simply stop and wait for the event to happen.

Interaction with an application by the user can generate several different kinds of events. Consider mouse events. If the mouse is in the drawing area, each movement generates a mouse movement event. If the user clicks a mouse button, a mouse button event is generated. A keystroke from the keyboard will generate a keyboard event.

If the mouse pointer is in a dialog, or over a menu or command button, then movement events are not generated. Instead, button clicks generate command events corresponding to the button or other control.

In a GUI environment, windows are usually not displayed alone. Often, other applications are running, each with its own windows. The host windowing system typically displays windows with various decorations that let the user manipulate the windows. Sometimes, these manipulations will generate events that require a response from the application code. For example, the user can use the mouse to change the size of a window causing a resize event. When multiple windows are displayed, some can be completely or partially covered by other windows. If the user moves a window so that a different part of the window is displayed, then an expose event is generated, which requires the program to redraw part of the canvas area.

All these events require a response from the application - to carry out the command, to draw something in the canvas area, or to redraw the canvas after a resize or expose event. Some events, however, are handled by the system, and not the application. This includes drawing menus and handling dialogs. For example, when a dialog is displayed, the system tracks mouse movements within the dialog, and handles redrawing the dialog for expose events. In general, the application is responsible for resize and expose events only for the canvas area.

All these events are asynchronous, and the application must be able to respond immediately to any of these events. The most common way to handle events is with what is called an event loop. Somewhere, there is an event loop that detects when the user presses a key, moves the mouse, or clicks a mouse button. This detection is usually done at the system level, and not by the program. As part of this process, the GUI tool kit will distinguish between different kind of events - a mouse click on a control, or a mouse motion over a drawing area, for example. These events are then passed on to the program for appropriate action. The exact method of handling the event-loop and passing the events on to the application code varies from toolkit to toolkit. In Swing, the program doesn't need any direct access to the event-loop. In Java, the event loop is really in a separate thread of execution which is started and controlled by the runtime system. A Java program handles events using what are known as listeners and callbacks. The section "Handling Swing Command Events" discusses events and Swing in more detail.

A Brief Introduction to Swing

The original GUI toolkit provided by Java was called the AWT (Abstract Windowing Toolkit). While there are many existing Java AWT applications, the AWT has been superseded by the Swing toolkit. Swing is generally more complete and easier to use than AWT. It is almost too complete. One of the problems of providing the GUI toolkit for a programming language is that it must provide everything, even if most of the features will be used by only a tiny fraction of programmers. The myriad of details and options provided by Swing make it seem more complicated that it has to be. We will try to focus on the essentials most Java programmers will need to use. With the proper perspective, it is possible to get a handle on Swing in just a few pages.

So, let's use our general GUI model to look at how Swing can build applications that fit that model. For the main application, Swing needs to provide the outer window that includes a title bar and special buttons, a menu bar, a tool bar, and place to hold the main view or views, often a drawing canvas.

While Swing has superseded the AWT, it is based upon and uses the AWT. To distinguish similarly named classes in the AWT, Swing has adopted its own naming conventions. For the most part, Swing classes start with a capital J, such as JFrame, JButton, or JSplitPane. However, not all Swing classes start with a J (e.g., Box or Timer). It is possible to mix AWT and Swing, but that really is not a good idea.

Swing provides the JFrame as the main class to support "standard" applications. A JFrame is a top-level container that exists mainly to provide a place for other Swing components to paint themselves. (Two other top-level components are also commonly used: JDialog for dialogs, and JApplet for applets.) A JFrame will be displayed with the title frame, and has a single component called a JRootPane, which has a menu bar (a JMenuBar), a content pane (which is typically set to hold something like a general purpose JPanel by the user program), and some other things that generally don't matter for typical situations. The fact that the JFrame uses a JRootPane isn't usually relevant for many practical situations.

It is really easy to create a simple Swing application with JFrame. The sequence of code that will create the practical object structure shown in Figure 5-3 is given in Listing 5-1.

Listing 5-1. Code excerpt
Code to setup JFrame
 ...
 // Setup a JFrame for practical use
  
     JFrame theFrame = new JFrame("Application Name");
  
     JMenuBar theMenuBar = new JMenuBar();
     // code to define items on Menu, event handlers, etc.
        ...
     theFrame.setJMenuBar(theMenuBar);
  
     JPanel thePanel = new JPanel();
     // code to define what is on panel, layout manager,
     // define user interface, command control, etc.
     // (non-trivial)
        ...
     theFrame.setContentPane(thePanel);
     theFrame.pack();
     theFrame.setVisible(true);
        ...
        

This code fragment would be found in the definition of some object. This would usually be defined in the top-level class that has the main method, and defines the application. It is likely that all this code would be found in the order shown here, but would have some code in between the steps to use the parts of the interface. But the steps shown are really at the heart of the matter:

1. Create JFrame
2. Create and define JMenuBar
3. Add JMenuBar to JFrame
4. Create JPanel
5. Add whatever you need to the JPanel
6. Add the JPanel to the JFrame Content Pane
7. Pack and show the JFrame

Figure 5-3.
The Practical structure of JFrame

In this example, note that the content pane has been set to a JPanel, which can be used as a general purpose window to hold buttons, drawing canvases, split panes - anything the program needs to display. Thus, to implement a tool bar, the JPanel can use a Border layout to hold a JToolBar at its top, and yet another drawing pane or panel in its center.

The key to this whole idea is that the JPanel can serve to build practically any interface needed. Swing has several kinds of panes (e.g., JPanel, JSplitPane, JTabbedPane) that can serve to hold other Swing components. Associated with each such pane is a layout manager. Layout managers control how other Swing components are placed in a pane. Some layout managers are relatively simple to understand, but others get quite complicated. But the difficulty is in the details of using a specific layout manager. The concept that each pane has a layout manager is simple.

Once you have this basic object structure down, it is "simply" a matter of selecting which Swing components you need to implement your interface. The basic components, such as JButton, are quite easy to use. Others, such as JTable are harder. And if you need to draw something on a drawing canvas, you will need to use the Java Graphics object. How to use Graphics is beyond the scope of this book other than to note that your program will be responsible for responding to paint events caused by the drawing pane being resized or exposed.

Defining the GUI and laying out the Swing components is not all that difficult. But it is only half the story. For each component that the user can interact with, your program must provide code that makes an appropriate response. These are the events, and the next section, "Handling Swing Command Events" shows how to handle events generated by Swing components.

It is important to remember that one of the hardest parts about using Swing is figuring out which features of a component are most useful and commonly needed, and which are included to give Swing the capability of defining interfaces for every possible situation. Sun's documentation for Java (and the rest of the core Java classes) is consistent and complete. It is really much better than average. But it covers everything. The Sun Swing Tutorial is more useful. It has many good examples which tend to demonstrate the most useful features of the various components. Once you get an idea of what you want to do with a component, the Swing documentation provided by Sun is about the best there is available.

Handling Swing Command Events

The components of Java Swing generate events in response to various actions. For example, when a user clicks on a button or selects a menu item, Swing will generate an ActionEvent. The program handles these events by registering an appropriate Listener for each event. There are really several kinds of events generated by Swing - ActionEvents, ChangeEvents, ItemEvents, and others. Each kind of event is appropriate for some kind of action in a specific component. These can be caused by a user click, a list changing its values, a timer going off, and different things depending on the nature of the component. All these events can be handled in the same general way.

The general steps are:

1. Create a component (such as a JButton)
2. Add it to an appropriate place in the GUI (such as a JPanel)
3. Register a Listener that gets notified when the component generates an event (such as an ActionListener for a user click)
4. Define the callback method that is invoked when the Listener is notified (such as actionPerformed for an ActionListener).

As with most programming tasks, these steps can be accomplished in many ways using Java. However, two main ways that have evolved as Java idioms to do this. One is to define a single listener, and determine which component generated the event using a series of if statements. The second method is to use inner classes to respond to events for each Swing component individually. And, there are really two variants of using inner classes. The first is to use anonymous inner classes, and the other is to define named inner classes.

We will define a simple Java application that displays two buttons (Figure 5-4), and responds to the user click by displaying an appropriate dialog box (Figure 5-5). However, this example can be extended to much more complicated user interfaces. We will implement this application three different ways - the single listener approach, the anonymous inner class approach, and the named inner class approach.

Figure 5-4.
Simple Swing Application

Figure 5-5.
Dialog Window - response to event

First, we will implement this program using the single listener approach. We will define a single class called Simple1. This contains all the code needed for this simple example. All user events (button clicks in this example - they could be menu picks or any other event) are handled by the callback actionPerformed in the single listener class SimpleListener.

Here's the code:

Listing 5-2. Simple1.java
Single listener
 /*
  * Simple1.java - an example of handling events.
  *    For this example, we implement a single ActionListener
  *    which is then used to catch all events. A series of ifs
  *    are used to determine which object caused the event.
  */
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
  
 public class Simple1
 {
     private static JFrame frame;   // static so main can use it
     private static JPanel myPanel; // panel for the contentPane
     private JButton button1;       // Define out here to make
     private JButton button2;       // visible to ActionListener
     
     public Simple1()               // Construct, build GUI
     {
         // Create a panel
         myPanel = new JPanel();
         // Create the buttons.
         button1 = new JButton("Button 1");  // Create buttons
         button2 = new JButton("Button 2");
  
         SimpleListener ourListener = new SimpleListener();
         // Set action listener for both buttons to share
         button1.addActionListener(ourListener);
         button2.addActionListener(ourListener);
  
         myPanel.add(button1);   // Adds to current JFrame
         myPanel.add(button2);
     }
  
     private class SimpleListener implements ActionListener
     {
     /*
      * We will use a simple inner class to implement an
      * ActionListener to use to get the button events. We
      * could have instead used the Simple1 class itself to
      * implement ActionListener instead, but this way is 
      * more consistent with the other examples.
      */
         public void actionPerformed(ActionEvent e)
         {
             // We will use getActionCommand to get the button
             // name, but we could use getSource() instead and
             // the if tests would then be like:
             // if (e.getSource() == button1)  etc.
  
             String buttonName = e.getActionCommand();
             if (buttonName.equals("Button 1"))
                 JOptionPane.showMessageDialog(frame,
                                             "Button 1 pressed");
             else if (buttonName.equals("Button 2"))
                 JOptionPane.showMessageDialog(frame,
                                             "Button 2 pressed");
             else
                 JOptionPane.showMessageDialog(frame,
                                             "Unknown event");
         }
     }
  
     public static void main(String s[])
     {
         Simple1 gui = new Simple1(); // Create Simple1 component
         
         frame = new JFrame("Simple1");  // JFrame for the panel
         // Standard idiom to catch close event
         frame.addWindowListener(new WindowAdapter() {
              public void windowClosing(WindowEvent e)
               {System.exit(0);} });
  
         frame.getContentPane().add(myPanel);
         frame.pack();                  // Ready to go
         frame.setVisible(true);
     }
 }

Let's go over how this code works. Our simple application will consist of a Swing JFrame defined in main. We will add a single JPanel to the frame. The panel will contain the two JButtons. The variable references to these components, frame, button1, and button2 are defined at the top level of Simple1.1

The main method starts by creating the Simple1 object, which then creates the user GUI by defining the JPanel and adding the JButtons. After each button is created, JButton.addActionListener is called for each button instance to add the single instance of SimpleListener. Finally, each button is added to the panel. After the GUI has been created, main adds the panel to the frame, and displays the results. When a user clicks one of the buttons, the actionPerformed callback is called, and inside if statements are used to determine which button was pressed, and an appropriate modal dialog box is displayed. The UML sequence diagram in Figure 5-6 shows the sequence of operations when a user clicks on Button1.

Figure 5-6.
Sequence Diagram for simple listener

This simple single listener approach has some problems. Because the determining the identity of each event requires either a string comparison (e.getActionCommand().equals("name")) or, alternatively, an object comparison (e.getSource() == button1), you can't use a switch, and must add an if for each event. This can result in a long string of if statements. And the code can get unwieldy and difficult to read and maintain. For handling just a few events, however, this approach is simple to use.

One solution to this problem is to use anonymous inner classes. Instead of implementing a single named inner class for the listener, you simply use an anonymous class as the argument to addActionListener. This places the definition of the event handler right with the definition of the control component.

Listing 5-3. Simple2.java
Anonymous inner classes for listeners
 /*
  * Simple2.java - an example of handling events.
  *    For this example, we will use anonymous inner classes to
  *    implement an ActionListener for each button. This approach
  *    can avoid long switch-type if statements from the approach
  *    used in the Simple1 example.
  */
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
  
 public class Simple2
 {
     private static JFrame frame;   // static so main can use it
     private static JPanel myPanel; // panel for the contentPane
     private JButton button1;       // Define out here to make
     private JButton button2;       // visible to ActionListeners
  
     public Simple2()               // Construct, build GUI
     {
         // Create a panel
         myPanel = new JPanel();
         // Create the buttons
         button1 = new JButton("Button 1");
         button2 = new JButton("Button 2");
  
         // For each component that needs a listener, define an
         // anonymous inner class to implement ActionListener.
         button1.addActionListener(
             new ActionListener()
             {
                 public void actionPerformed(ActionEvent e)
                 {
                     JOptionPane.showMessageDialog(frame,
                                            "Button 1 pressed");
                 }
             }
         );
  
         button2.addActionListener(
             new ActionListener()
             {
                 public void actionPerformed(ActionEvent e)
                 {
                     JOptionPane.showMessageDialog(frame,
                                             "Button 2 pressed");
                 }
             }
         );
  
         myPanel.add(button1);        // Adds to current JFrame
         myPanel.add(button2);
     }
  
     public static void main(String s[])
     {
         Simple2 gui = new Simple2(); // Create Simple2 component
         
         frame = new JFrame("Simple2");  // JFrame for the panel
         // Standard idiom to catch close event
         frame.addWindowListener(new WindowAdapter() {
              public void windowClosing(WindowEvent e)
               {System.exit(0);} });
         frame.getContentPane().add(myPanel);
         frame.pack();                  // Ready to go
         frame.setVisible(true);
     }
 }

Using anonymous inner classes has some problems. First, the definitions of the classes, and thus the code that handles the events, can be scattered all over the application source code, depending on where the component was defined. They tend to be defined deep within the nested code, which can make the indentation and appearance of the code a bit awkward. If the required response to the event is somewhat complicated, the code for the anonymous class can get long, thus making it difficult to follow the overall flow and intent of the code that is defining the components in the first place. Finally, there are often synonyms for commands defined for a particular user interface. For example, it is quite common to provide both a tool bar button and a menu item for the same command.

The sequence diagram in Figure 5-7 shows the event sequence with an anonymous listener. The only difference from the previous case is that the anonymous listener is called instead.

Figure 5-7.
Sequence diagram with anonymous listener

Using named inner classes provides a good alternative to anonymous inner classes. All the handlers can be defined in one place. They can have meaningful names. A single handler can be reused by different controls. And the code where the GUI is defined and laid out is not interrupted by long anonymous class definitions. So here is the simple application again, this time written using named inner classes.

Listing 5-4. Simple3.java
Named inner classes for listeners
 /*
  * Simple3.java - an example of handling events.
  *    For this example, we will use inner member classes to
  *    implement an ActionListener for each button. This approach
  *    can avoid some of the code clutter that anonymous classes
  *    can sometimes cause. It also concentrates the action code
  *    all in one place, and allows synonyms.
  */
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
  
 public class Simple3 extends JPanel
 {
     private static JFrame frame;    // static so main can use it
     private static JPanel myPanel;  // a panel for contentPane
     private JButton button1;        // Define out here to make
     private JButton button2;        // visible to ActionListener
     
     // Define handlers for each event needed (button1, button2)
     private class Button1Handler implements ActionListener
     {
         public void actionPerformed(ActionEvent e)
         {
             JOptionPane.showMessageDialog(frame,
                                          "Button 1 pressed");
         }
     }
     
     private class Button2Handler implements ActionListener
     {
         public void actionPerformed(ActionEvent e)
         {
             JOptionPane.showMessageDialog(frame,
                                          "Button 2 pressed");
         }
     }
     
     public Simple3()                // Construct, build GUI    
     {
         // Create a panel
         myPanel = new JPanel();
         // Create the buttons
         button1 = new JButton("Button 1");
         button2 = new JButton("Button 2");
  
         // For each component add it ActionListener class
         button1.addActionListener(new Button1Handler());
         button2.addActionListener(new Button2Handler());
  
         myPanel.add(button1);        // Adds to current JFrame
         myPanel.add(button2);
     }
  
     public static void main(String s[])
     {
         Simple3 gui = new Simple3();  // Simple3 component
  
         frame = new JFrame("Simple3");  // JFrame for the panel
         // Standard idiom to catch close event
         frame.addWindowListener(new WindowAdapter() {
              public void windowClosing(WindowEvent e)
               {System.exit(0);} });
  
         frame.getContentPane().add(myPanel);
         frame.pack();                   // Ready to go
         frame.setVisible(true);
     }
 }

The sequence diagram in Figure 5-8 now shows that the named listener Button1Handler is used when a user clicks Button1.

Figure 5-8.
Sequence diagram for named inner class listener

In this example, we are responding to the action event generated when the user clicks on one of the buttons. The way these events are handled applies equally well to any Swing event - a menu pick, a list selection, and others. Some of these are not ActionEvents with ActionListeners, but include ItemEvents with ItemListeners, and ChangeEvents with ChangeListeners, and so on. The general approaches shown in these examples will work for all.

A Bunch of Options

On the whole, using the basic features of Swing components is not overly complex. But, you won't always want just the basic features. Most components have many options that can be used. Take a JButton, for example. The simplest JButton has a simple text label and takes the default appearance of the GUI look-and-feel. But, there are many options. You can make it the default button. You can change the borders. You can change the color. You can add an icon to the button. You can enable or disable the button. You can add a tool tip. You can change its size. You can change the font. And so on. This pattern repeats, with some overlap, for almost every Swing control.

Aside from making it harder to fully understand each component, each one of these options is implemented by a specific method call to the Swing component's class. This means that any code that fully takes advantages of a component's options can get long. If we set just the options we mentioned for a JButton, it would take at least nine lines of code to define a JButton object. Repeat this for each control, and the code for defining a GUI can get long and ugly.

There are some solutions to this problem. The definitions for components can be isolated somewhat from the logic. Rather than building a whole interface in the middle of code that has some higher level meaning, you can place the definitions in some method such as createGUI, and then just call it from the higher level code. That still means there will be long sequences of repetitious code somewhere.

If you find you are setting the same component attributes over and over again, you can implement small helper methods that take the attributes as parameters, and then set them all. Then the code for defining the component would be a single line call to the helper method, rather than many lines.

Even so, defining components of the user interface can be complex, and require several lines of code. Even though this can make the code longer and more complex, it does keep it all in one place, and portable across the systems Java supports.

One alternative used by other GUI toolkits is to use resource definition files. These resource files have their own set of problems. Another alternative is to use software that lets you define GUIs interactively, and then generates all the code automatically. Many Java tools will let you do this, in fact. But even that approach has some serious problems, which we will discuss in Chapter 10.

MVC: Model, View, Controller

A commonly used design architecture for GUI applications is called Model-View-Controller, or MVC. MVC was originally developed for the Smalltalk language, but has found widespread use for designing GUI applications in any object-oriented language. When a GUI application works with an object, it can usually be represented with a model. The model is a complete representation of the object being used by the application. This could be a word processing document, a graphical image, a spreadsheet, or a database. The key concept is that the model is self-contained, and its representation is independent of the rest of the program. The model then provides the manipulation methods required for the outside world to use the object the model implements.

An application will then consist of any number of views of the model. There might be an edit view and a print view of a document, or several views of different pages of the document for example. Each view tracks what it needs to know about its particular perspective of the model, but each view will interact with the same model. Finally, each view has associated with it a user interface implemented as a controller object. This might include command buttons, handlers for the mouse, and so on. When a controller gets a command from the user, it uses appropriate information from the associated view to modify the model. Whenever the model changes, it will notify all views, which then update themselves.

This is an elegant architecture, and is shown in Figure 5-9. Note that there can be as many views as needed. Logically, each view has an associated controller. Typically there will be a single Model class, and several View classes with their associated Controller classes. The Controller will respond to actions from the user, use whatever information it needs from the associated View, and send a message to the Model to modify itself. When a Model changes (and this can be caused by events other than user commands, such as asynchronous events generated by a shared database being modified), it notifies all Views, which then update the display.

Figure 5-9.
Model-View-Controller

In practice, especially in Java, the View and Controllers are often combined. This is usually due to the design of the GUI toolkit, such as Swing. So a Java MVC program will really be a Model and associated View/Controllers made up of specifications of Swing components and their listeners.

No matter what the actual implementation of the View/Controller is, the main idea of MVC is to separate the model from its display and the user interface. There are many advantages to this approach. The model can be designed completely independently of how it will be displayed. With such an independent model, there will then be great flexibility in how views can be designed to display the model. New views can be created, and the whole user interface changed without affecting the model.

CRC Cards for MVC

The MVC design lends itself as a good example for CRC cards. Note in this example how the View and Controller cards overlap, showing their close collaboration. The Model is placed under the View/Controller, showing their supervision. These cards show only the minimum responsibilities to help clarify the model. Note that when using any tool such as CRC Cards or the UML, the tool should be used to make it easier to understand the design, which often means showing only the details that are relevant.

MVC with Java

In the original Smalltalk MVC concept each of the classes - Model, View, Controller - was an integral part of the Smalltalk implementation. Other programming languages, and specifically Java, do not provide integrated MVC classes. However, the concept of MVC is still valid, and provides a good way to implement GUI programs.

As we discussed earlier, a graphical interface in Swing is usually built by using several Swing components, and defining the appropriate listeners for each component. Thus, it is often easier to define a combined View/Controller class when working with Swing. There can be a pure, Swing-free implementation of the Model class. Views then consist of implementations of the different views of the model using Swing components, and the controllers are implemented using the required listener interface of Swing. Thus, the controllers will really be the required listeners for the various Swing components used in the views. The controllers will be included with the associated view code. It is possible to use a Controller class that further separates the controllers from the views, and we will develop such a Controller class in the next section.

While MVC is most commonly used for full GUIs, it can be applied to other designs. At their lowest level, many of the individual Swing components are implemented using MVC. This provides a great deal of programming flexibility for using Swing components - the user can provide their own look and feel, for example, by replacing the views. However, this flexibility is not required or used by the vast majority of Swing applications. Nor should the MVC implementation of Swing be confused with an MVC implementation of an application that uses Swing. Swing provides nothing that makes it easier to implement an MVC application, even though its own low-level implementation uses MVC.

In the next section, we will present the design of a small Swing based MVC framework. This framework combines many of the aspects of Swing that we've covered. It defines a top-level application class that uses a JFrame to build the foundation of the application that includes a menu bar, tool bar, and a JPanel to construct views. It implements Model, View, and Controller classes that can be easily used to construct a full MVC application. And it provides methods that make it easier to use and define controls within the views.

A Small Swing MVC GUI Framework

Now we are ready to put everything from this chapter together in a small Swing based MVC framework. Even though it is small, this framework is suitable as a starting point for even large Java MVC applications.

Recall the definition of a framework at the beginning of this chapter. One of the goals of a framework, even a simple one, is to make it easier for a programmer to develop an application that requires the services of the framework. In this case, the goal of our simple framework is to make it easier to develop a Swing GUI application that uses the MVC design.

We will begin by considering what services the framework must include to build a "standard" GUI application as described in "Graphical User Interfaces" on page 118. Such a standard app included an outer frame that contained a title bar, a menu bar, a tool bar, and a drawing area that could be used for views. The existing Swing JFrame class almost meets these requirements, so we will define a top-level application class based on JFrame. Most frameworks have a name, and we'll call our framework the Wmvc framework for the Wampler MVC framework. All classes in this framework will be start with Wmvc. This kind of naming convention is commonly used with frameworks and libraries.

WmvcApp Class

The top-level application class is called WmvcApp. A user application will derive its top-level application class from WmvcApp. The basic structure of the Wmvc framework is shown in Figure 5-10. The class diagram for WmvcApp shows the most important services it provides. WmvcApp allows the user app class to build an application easily. The app will define the elements of the menu bar, tool bar, and drawing panes. The user app defines menus by creating JMenu objects, and then adding items to those menus using the Wmvc menu controllers, and finally adding the menus to the menu bar with WmvcApp.addMenu. Tool bar items are defined and added to the tool bar using Wmvc controller objects. WmvcApp provides a JPanel for the app to define whatever is needed for views.

WmvcApp provides the basic functionality needed to build simple "standard" applications. Although it is not that complicated, it simplifies the job of the programmer. The details of defining a tool bar and a JPanel are hidden.

WmvcApp has another job - to support the MVC model. While the structure of an MVC program is clear, the MVC structure must really hang off of something. In the Wmvc framework, the WmvcApp class provides the foundation to build the MVC structure on. Because all the views must access the model, WmvcApp provides the services setModel() and getModel() to give them access to the model. It provides a class method to get access to the WmvcApp object itself. WmvcApp does not provide direct support for views. Instead, views are created in the derived WmvcApp class directly. The Thermometer example later will help make this clear.

The details of creating the tool bar and drawing canvas are found in the WmvcApp.java code. In addition to the services already discussed, WmvcApp provides a standard way to handle closing an application. When the user presses the close button on the title bar, the method appClosing is called, and will exit by default. The user app can control closing behavior by overriding appClosing. There is a method to cause the app window to be displayed after it has been defined (showApp()). Since the programmer must derive an app class from WmvcApp, it has been declared abstract.

Figure 5-10.
Wmvc framework

UML: We've introduced the note symbol in Figure 5-10. It is used to show notes that help explain the diagram. Note how UML diagrams don't have to give all the details. Sometimes they are used to show just the overall relationships between classes, and don't show any details of the attributes or operations. Other times, they can show only relevant operations. And finally, they can be used to show the full details, including all attributes and operations.


Listing 5-5. WmvcApp.java
Top level base class for Wmvc Application
 /*
  * WmvcApp - The main Application Class for Wmvc
  * Copyright (c) 2001, Bruce E. Wampler
  */
  
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
  
 public abstract class WmvcApp
 {
     private static WmvcApp theApp = null;       // Singleton
  
     // Model
     
     protected static WmvcModel theModel = null;
     
     // GUI interface parts
     private static JFrame theFrame = null;  // The topmost frame
     private static JMenuBar theMenuBar = null;
     private static JToolBar theToolBar = null;
     private static JPanel theContentPanel = null;
    
     public static JFrame getFrame() { return theFrame; }
     public static JMenuBar getMenuBar() { return theMenuBar; }
     public static JToolBar getToolBar() { return theToolBar; }
     public static JPanel getContentPanel()
         { return theContentPanel; }
     public static WmvcModel getModel() { return theModel; }
     public static void setModel(WmvcModel m) { theModel = m; }
     
     public static WmvcApp getApp()
     {
         return theApp;
     }
     
     // Only one constructor. Could have alternates:
     //        public WmvcApp()
     //        public WmvcApp(String aName)
     public WmvcApp(String aName, boolean cMenu, boolean cTool)
     {
         if (theApp != null)
             return;
         theApp = this;
         initializeWmvc(aName,cMenu,cTool);
     }
     
     private void initializeWmvc(String aName, boolean cMenu,
                                 boolean cTool)
     {
         // Step 1 - set up the main JFrame
         theFrame = new JFrame(aName);
  
         // handle closing cleanly
         theFrame.setDefaultCloseOperation(
                                     JFrame.DO_NOTHING_ON_CLOSE);
         theFrame.addWindowListener(new WindowAdapter()
           {
               public void windowClosing(WindowEvent e)
               { 
                   if (theApp.appClosing())
                       System.exit(0);
               } 
           });
  
         // Step 2 - the menu bar
         if (cMenu)              // add a MenuBar?
         {
             theMenuBar = new JMenuBar();
             theFrame.setJMenuBar(theMenuBar);
         }
  
         // Step 3 - the JPanel (may or may not have a tool bar)
 
         theContentPanel = new JPanel();             // A JPanel
         theContentPanel.setLayout(new BorderLayout());
         theContentPanel.setPreferredSize(
                 new Dimension(400, 300));
  
         if (cTool)              // add a Tool Bar?
         {
             theToolBar = new JToolBar();
             theContentPanel.add(theToolBar,BorderLayout.NORTH);
         }
  
         // Note: we could add a status bar (another ToolBar) at
         // the bottom by using BorderLayout.SOUTH
  
         theFrame.setContentPane(theContentPanel);
     }
     
     public static void addMainPane(JComponent pane)
     {
         // This will add the "user" pane to the content pane
         theContentPanel.add(pane,BorderLayout.CENTER);
     }
     
     public static void addMenu(JMenu menu)
     {
         if (theMenuBar == null)
             return;
         theMenuBar.add(menu);
     }
     
     public static void showApp()
     {
         theFrame.pack();
         theFrame.setVisible(true);
     }
     
     public boolean appClosing()
     {
         return true;  // Default behavior - close automatically
     }
 }

WmvcView, WmvcController, WmvcModel Classes

The MVC classes themselves are not too complicated. One of the most important responsibilities of these classes is to allow the model to update all the views whenever it changes. This is implemented by using the Java Observable/Observer classes2. WmvcView implements the update method of the Observer Java interface as updateView. Each view then registers itself with the model using the WmvcModel method addView, which uses the addObserver method defined by the Java Observable class that WmvcModel extends. When the model changes, the model object uses notifyViews to send a message to all views that have registered with the model. The Observable class initiates calling the updateView method of each registered view. The Wmvc classes insulate the application programmer from the details of using the Observable class, and provide a naming convention more meaningful to use with the MVC design.

Listing 5-6. WmvcModel.java
Wmvc Model base class - the Model
 /*
  * WmvcModel - An MVC model superclass for the Wmvc Framework
  * Copyright (c) 2001, Bruce E. Wampler
  */
  
 import java.util.*;
  
 public class WmvcModel
     extends     Observable
 {
     // not a huge class, but provides implementation of
     // Observer pattern using MVC naming
  
     public WmvcModel()
     {
         super();
     }
     
     public void addView(WmvcView view)
     {
         addObserver((Observer) view);
     }
     
     public void deleteView(WmvcView view)
     {
         deleteObserver((Observer) view);
     }
     
     public void notifyViews()
     {
         setChanged();
         notifyObservers();
     }
 }
Listing 5-7. WmvcView.java
Wmvc View base class - the View
 /*
  * WmvcView - An MVC view superclass for the Wmvc Framework
  * Copyright (c) 2001, Bruce E. Wampler
  */
  
 import java.util.*;
  
 public class WmvcView
     implements  Observer
 {
     // implement the single Observer class
     public void update(Observable observed, Object value)
     {
         updateView();
     }
     
     public void updateView()
     {
         // no-op by default
     }
 }

Wmvc includes the WmvcController class. This class is designed specifically to make it easier to build controllers for menu items and tool bar buttons3. The WmvcController class serves as a base class for subclasses that support specific menu bar and tool bar controls. This example has been simplified to implement only basic text and check box menu items (WmvcMenuItemCtl and WmvcChkMenuItemCtl), and simple buttons (WmvcTBButtonCtl) for the tool bar. Additional subclasses of WmvcController would be required to support additional Swing components on the tool bar, but have not been implemented to help simplify the example. Note that WmvcController is intended to simplify working with the menu bar and the tool bar. Controllers for Swing components inside the individual views would need to be implemented using Swing listeners defined within the view code. It would be possible, however, to design other WmvcController implementations for components not on the menu or tool bars. As we noted earlier, using MVC with Java often means a combined View/Controller.

The WmvcController supports functionality for subclasses that will implement menu items and tool bar controls. The basic support includes handling tool tips, and providing the implementation for the WmvcExecutor class. There is a subclass for each type of Swing component: normal menu, check box menu, and tool bar button. It would be simple to support other Swing components by adding new subclasses of WmvcController. There are many details that are "hidden" in this code to simplify using these components for the framework user.

Listing 5-8. WmvcController.java
The Controller
 /*
  * WmvcController - implements a general purpose Swing based
  *     Controller using the Command pattern - for Wmvc framework
  * Used to simplify Swing controls
  *
  * (c) 2001, Bruce E. Wampler
  */
  
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 import javax.swing.event.*;
  
 public class WmvcController
         implements      ActionListener,
                         ItemListener
 {
     protected JComponent myComponent;
     private WmvcExecutor wmvcExecutor;  // The Executor object
  
     // This constructor is use by the subobjects
     public WmvcController(JComponent comp,    // the component
                           String tip,
                           WmvcExecutor wExec)
     {
         myComponent = comp;
         wmvcExecutor = wExec;
         if (tip != null)
             myComponent.setToolTipText(tip);
     }
  
     public WmvcExecutor getWmvcExecutor()
         { return wmvcExecutor; }
  
     /* ------------------------------------------------------
      * Implement the Listeners for components.
      * Each listener will send a message to the appropriate
      * execute method from the associated WmvcExecutor. Type
      * of event determines the signature of the execute method.
      */
  
     // implment the ActionListener
     public void actionPerformed(ActionEvent event)
     {
         if (wmvcExecutor != null)
             wmvcExecutor.execute(event);
     }
  
     // implement ItemLisetener
     public void itemStateChanged(ItemEvent event)
     {
         if (wmvcExecutor != null)
             wmvcExecutor.execute(event);
     }
 }

The following are subclasses of WmvcController and implement menu items, checked menu items, and tool bar buttons.

Listing 5-9. WmvcMenuItemCtl.java
Controller subclass for JMenuItem
 /*
  * WmvcMenuItemCtl - implements JMenuItem controller
  * (c) 2001, Bruce E. Wampler
  */
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 import javax.swing.event.*;
  
 public class WmvcMenuItemCtl extends WmvcController
 {
  
     private JMenu myMenu;
     private JMenuItem menuItem;
     
         // Constructor for JMenu item: Standard JMenuItem
     public WmvcMenuItemCtl(JMenu menu,       // JMenu I go with
                         String text,        // Button's text
                         String icon,        // the icon's name
                         char mnemonic,      // Button's mnemonic
                         String accel,       // accelerator
                         WmvcExecutor wExec)
     {
         super((JComponent)new JMenuItem(), null, wExec);
         
         myMenu = menu;
         menuItem = (JMenuItem) myComponent;
  
         if (text != null)
             menuItem.setText(text);
         if (mnemonic != ' ' && mnemonic != 0)
             menuItem.setMnemonic(mnemonic);
         if (accel != null)
           {
             KeyStroke ks = KeyStroke.getKeyStroke(accel);
             menuItem.setAccelerator(ks);
           }
         if (icon != null)
           {
             Icon theIcon = new ImageIcon(icon);
             menuItem.setIcon(theIcon);
           }
         menuItem.addActionListener(this);  // Add listeners
         menuItem.addItemListener(this);
         menu.add(menuItem);              // Finally, add to menu
     }
   
     public void setEnabled(boolean enable)
     {
         menuItem.setEnabled(enable);
     }
  
     public JMenu getJMenu() { return myMenu; }
     public JMenuItem getJMenuItem() { return menuItem; }
 }

Listing 5-10. WmvcChkMenuItemCtl.java
Controller subclass for JCheckBoxMenuItem
 /*
  * WmvcChkMenuItemCtl - implements JCheckBoxMenuItem controller
  * (c) 2001, Bruce E. Wampler
  */
  
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 import javax.swing.event.*;
  
 public class WmvcChkMenuItemCtl extends WmvcController
 {
     private JMenu myMenu;
     private JCheckBoxMenuItem checkBoxItem;
  
     
     // Constructor for JMenu item:  JCheckBoxMenuItem
     public WmvcController(JMenu menu,       // JMenu I go with
                         String text,        // Button's text
                         String icon,        // the icon's name
                         char mnemonic,      // Button's mnemonic
                         String accel,       // accelerator
                         boolean checked,    // initial state
                         WmvcExecutor wExec)
     {
         super((JComponent)new JCheckBoxMenuItem(), null, wExec);
         
         myMenu = menu;
         checkBoxMenuItem = (JCheckBoxMenuItem) myComponent;
         if (text != null)
             checkBoxItem.setText(text);
         if (mnemonic != ' ' && mnemonic != 0)
             checkBoxItem.setMnemonic(mnemonic);
         if (accel != null)
           {
             KeyStroke ks = KeyStroke.getKeyStroke(accel);
             checkBoxItem.setAccelerator(ks);
           }
         if (icon != null)
           {
             Icon theIcon = new ImageIcon(icon);
             checkBoxItem.setIcon(theIcon);
           }
  
         checkBoxItem.setState(checked);
  
         checkBoxItem.addActionListener(this);   // Add listeners
         checkBoxItem.addItemListener(this);
         myMenu.add(checkBoxItem);        // Finally, add to menu
     }
  
     public boolean getState()
     {   return checkBoxItem.getState();
     }
     public void setState(boolean checked)
     {   checkBoxItem.setState(checked);
     }
     public void setEnabled(boolean enable)
     {   checkBoxItem.setEnabled(enable);
     }
     public JMenu getJMenu() { return myMenu; }
     public JCheckBoxMenuItem getJCheckBoxMenuItem()
     {   return checkBoxItem;
     }
 }

Listing 5-11. WmvcTBButton.java
Controller subclass for JButton for tool bar
 /*
  * WmvcTBButtonCtl - implements JButton controller for tool bar
  * (c) 2001, Bruce E. Wampler
  */
  
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 import javax.swing.event.*;
  
 public class WmvcTBButtonCtl extends WmvcController
 {
     private JButton myButton;
     
     // Constructor for JToolBar item:  JButton
     public WmvcTBButtonCtl( String text,        // Button's text
                             String icon,        // the icon's name
                             String tip,         // tool tip
                             WmvcExecutor wExec)
     {
         super((JComponent)new JButton(), tip, wExec);
         
         myButton = (JButton) myComponent;
     
         if (text != null)
             myButton.setText(text);
         if (icon != null)
           {
             Icon theIcon = new ImageIcon(icon);
             myButton.setIcon(theIcon);
           }
  
         myButton.addActionListener(this);       // add listener
         WmvcApp.getToolBar().add(myButton);     // add to bar
     }    
  
     public void setEnabled(boolean enable)
     {   myButton.setEnabled(enable);
     }
     public JButton getJButton() { return myButton; }
 }

WmvcExecutor is the implementation specification for the Command design pattern. In the context of this MVC framework, Executor seemed to be a more appropriate name, even though it is based on the Command pattern. It is used and functions much like a standard Swing listener.

Listing 5-12. WmvcExecutor.java
The Executor (Command pattern) used with WmvcController
 /*
  * WmvcExecutor - Support class for implementation
  *   of the WmvcController class.
  * (c) 2001, Bruce E. Wampler
  */
  
 import java.awt.event.*;
 import javax.swing.event.*;
  
 public class WmvcExecutor
 {
     // an execute for each type of event we might have
     public void execute(ActionEvent event)
     {
     }
     public void execute(ItemEvent event)
     {
     }
     public void execute(ChangeEvent event)
     {
     }
 }

Now that we've examined the implementation of the Wmvc framework, we will present a small example that uses the framework.

A Simple Application Based on Wmvc

We will use a small application called Thermometer to illustrate how the Wmvc framework can be used. This application will display a temperature value in both degrees Fahrenheit and degrees Celsius. It will provide buttons on the tool bar to allow the user to move the temperature up and down.

Figure 5-11.
Thermometer App

The model, called ThermometerModel, is simple. It simply is an integer value representing the Fahrenheit temperature. It has a getter to get the current temperature, and a setter to set a new temperature. Whenever the temperature changes, the model will notify all views.

There are three views. The most obvious are the display views which display the temperature in Fahrenheit and in Celsius. The third view is doesn't actually display the temperature. It is a control view that reacts to menu and tool bar commands. This non-display view is really just a controller, but it is implemented as a view/controller to fit within the MVC design. The two display views are really two objects of the class TemperatureView. These views don't have a controller. The non-display view is called MainView, and it uses the WmvcController class to implement the controller.

Figure 5-12 is the UML diagram of the Thermometer application. Note that this diagram is really somewhat simplified to show the logical structure of the app. We don't show the WmvcApp, WmvcModel, or WmvcView classes as they are unneeded detail. The update, modify, and draw-in associations have been removed to simplify the diagram.

Figure 5-12.
Thermometer UML

The code for the application is really quite simple. That was the goal of designing the Wmvc framework to begin with. Listing 5-13 shows the Thermometer class, which is the app class derived from WmvcApp. Thermometer defines the app by creating the model, the views, and adding the Fahrenheit and Celsius views to a JSplitPane.

Listing 5-13. Thermometer.java
Thermometer subclass of WmvcApp
 /*
  * Thermometer - A simple test for Wmvc Framework
  * Copyright (c) 2001, Bruce E. Wampler
  */
  
 import java.awt.*;
 import javax.swing.*;
  
 public class Thermometer extends WmvcApp
 {
     private MainView mainView;
     private TemperatureView fView;
     private TemperatureView cView;
    
     public Thermometer(String name)     // constructor
     {
         super(name, true, true);    // Create with menu, toolbar
  
         // **** First, create the model
         setModel( (WmvcModel) new ThermometerModel() );
  
         // **** Next, create the view/controllers
         mainView = new MainView();      // won't use any panels
         fView = new TemperatureView('F');  // Fahrenheit, left
         cView = new TemperatureView('C');  // Celsius, right
  
         // ****  Create a split pane, add list and item views
         JSplitPane splitPane = new JSplitPane(
             JSplitPane.HORIZONTAL_SPLIT, fView.getPanel(),
             cView.getPanel());
         splitPane.setOneTouchExpandable(false); // details
         splitPane.setDividerLocation(150);
  
         WmvcApp.addMainPane(splitPane); // add splitter
     }
     
     public static void main(String[] args)
     {
         final Thermometer theThermometer = 
                 new Thermometer("Thermometer App");
  
         WmvcApp.getContentPanel().setPreferredSize(
                 new Dimension(300, 60)); // not big
         WmvcApp.showApp();
  
         ((ThermometerModel) (WmvcApp.getModel())).setTemp(70);
     }
 }

The model for this application is extremely simple. It has one attribute: the temperature. The views and controller interact with the model using setTemp and getTemp. The model updates views when setTemp changes the value.

Listing 5-14. ThermometerModel.java
Model subclass
 /* 
  * ThermometerModel - implements model. Handles changes.
  * Copyright 2001, Bruce E. Wampler
  */
  
 import java.util.*;
  
 public class ThermometerModel
     extends     WmvcModel
 {
     private int theTemp;
     public int getTemp() { return theTemp; }
  
     public ThermometerModel()
     {
         theTemp = 0;
     }
  
     public void setTemp(int newTemp)
     {
       theTemp = newTemp;
       notifyViews();            // not much to the model!
     }
 }

The MainView defines the menu items and the tool bar items. Since it doesn't have to change anything when the model changes, its uses the default WmvcView update method, which doesn't do anything.

Listing 5-15. MainView.java
Main view subclass
 /*
  * MainView - Top level view/controller for the Thermometer
  *
  * This is the main view/controller.
  * Copyright (c) 2001, Bruce E. Wampler
  */
  
 import java.util.*;
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
  
 public class MainView extends WmvcView
 {
     private ThermometerModel myModel; // local model reference
  
     public MainView()
     {
         myModel = (ThermometerModel) WmvcApp.getModel();
         myModel.addView(this);
         JMenu fileMenu = new JMenu("File");
  
         // File->Up
         WmvcMenuItemCtl fileUp = new WmvcMenuItemCtl(fileMenu,
             "Up",null, 'U',
              null /* no accel */, new WmvcExecutor()
             {
                 public void execute(ActionEvent event)
                 {
                     myModel.setTemp(myModel.getTemp()+1);
                 }
             });
  
         // File->Down
         WmvcMenuItemCtl fileDown = new WmvcMenuItemCtl(fileMenu,
             "Down",null, 'D',
             null /* no accel */, new WmvcExecutor()
             {
                 public void execute(ActionEvent event)
                 {
                     myModel.setTemp(myModel.getTemp()-1);
                 }
             });
  
         // File->Exit
         WmvcMenuItemCtl fileExit = new WmvcMenuItemCtl(fileMenu,
             "Exit", null, 'x',
             null /* no accel */,  new WmvcExecutor()
             {
                 public void execute(ActionEvent event)
                 {
                     if (WmvcApp.getApp().appClosing())
                       System.exit(0);
                 }
             });
  
         WmvcApp.addMenu(fileMenu);      // Add to app menu
  
         // ToolBar: Up
         WmvcTBButtonCtl toolUp = new WmvcTBButtonCtl(
             "    Up    ",null,"Up one degree F",
             fileUp.getWmvcExecutor() /* same as file up */);
  
         // ToolBar: Down
         WmvcTBButtonCtl toolDown = new WmvcTBButtonCtl(
             " Down ",null,"Down one degree F",
             fileDown.getWmvcExecutor() /* same as file up */);
     }
 }

There is one view class for both the Fahrenheit and Celsius views. The constructor determines which scale will be used. It overrides the WmvcView update method to handle the change notification from the model.

Listing 5-16. TemperatureView.java
View subclass for Fahrenheit and Celsius views
 /*
  * TemperatureView - a view for C or F temps.
  * Copyright (c) 2001, Bruce E. Wampler
  */
  
 import java.util.*;
 import java.awt.*;
 import javax.swing.*;
  
 public class TemperatureView extends WmvcView
 {
     private JPanel myPanel;
     private ThermometerModel myModel;           // local copy
     private char myScale;
  
     private static JLabel lblF = new JLabel("Fahrenheit: ");
     private static JLabel lblC = new JLabel("Celsius: ");
     private JLabel fldTemp;
  
     public JPanel getPanel() { return myPanel; }
  
     public TemperatureView(char scale)
     {
         myModel = (ThermometerModel)WmvcApp.getModel();
         myModel.addView(this);          // adds update for mvc
  
         myScale = scale;                // remember my scale
  
         myPanel = new JPanel();         // surrounding Panel
         myPanel.setPreferredSize( new Dimension(390, 40));
  
         if (scale == 'F' || scale == 'f')
         {
             myPanel.add(lblF);                  // F degrees
         }
         else
         {
             myPanel.add(lblC);                  // C degrees
         }
         fldTemp = new JLabel(" ");
         fldTemp.setForeground(Color.black);     // temp in black
         myPanel.add(fldTemp);
     }
  
     public void updateView()
     {
         int val = myModel.getTemp();
         if (myScale == 'C')
             val = (int) (((double)(val-32))/1.8); // not rounded
         fldTemp.setText(Integer.toString(val));
     }
 }

UML Sequence Diagram for Thermometer

Figure 5-13 is the UML Sequence Diagram corresponding to the user clicking the Up Button. The sequence diagram shows how the Wmvc framework implements MVC to send messages to the views.

Figure 5-13.
UML Sequence Diagram - Up button pressed
When the user clicks the Up button, the object corresponding to the Up button, called upButton in the diagram, sends an actionPerformed message to the Up button controller, toolUp4. The toolUp controller then sends a setTemp message to the model, myModel, to increase the temperature by 1. After changing its internal state appropriately, theModel then sends the notifyViews message. This is effectively a message to itself (using the Observer design pattern), which then causes the update method of each View to be called. The views then change their labels appropriately.

Chapter Summary

Resources

Swing

Creating a GUI with JFC/Swing, from Sun: java.sun.com/docs/books/tutorial/uiswing/index.html.

The JFC Swing Tutorial: A Guide to Constructing GUIs, Kathy Walrath, Mary Campione, Addison-Wesley, 1999, ISBN: 0-201-43321-4.

1
Note how this code follows the setup code for JFrames we outlined in Listing 5-1. It creates and adds JButtons to a JPanel (and implements the listeners for the buttons). The then creates a JFrame, and sets the content pane to the JPanel just created. The order of creation is not important. This app has no menus, so JMenuBar was not used.

2
The Observable class implements the Observer design pattern. Design patterns are discussed in more detail in Chapter 7. The examples that follow use other design patterns, which we will note.

3
WmvcController is implemented using the Command design pattern. See Chapter 7.

4
The Up button object, which we call upButton, is actually created by creating a WmvcController object. The framework hides the button object from the application. The corresponding controller is called toolUp.


TOC PREV NEXT INDEX

The Essence of Object-Oriented Programming with Java and UML (DRAFT)

Copyright © 2001 by Addison-Wesley

All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form, or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior consent of the publisher. You may view this document on a web browser, but you must not mirror or make local copies.