The Temperature Converter

Demo

The temperature converter GUI consists of two labels and two text fields. Typing a temperature into the Celsius field and then pressing the "Enter" key causes the equivalent Fahrenheit temperature to appear in the Fahrenheit field and vice versa.

Typing a non-numeric value into either field causes the following error dialog to appear:

Design

Model, View, and Controller are just roles to be played by user-defined classes. In this application the role of Model is played by TempConverter. The role of View is played by ConverterGUI, and the role of Controller is played by ConverterController.

All views extends Swing's JPanel class. All controllers implement AWT's ActionListener interface:

Implementation

The Model

TempConverter plays the role of model and is very simple:

TempConverter.java

The View

ConverterGUI plays the role of view. It extends JPanel and contains two text fields and a title. It also contains references to the model and controller.

ConverterGUI.java

One point of interest in the ConverterGUI is the reusable display method. This method creates a top-level or desktop JFrame to hold this JPanel, then sets visibility to true. This launches a parallel thread

   public void display() {
       JFrame frame = new JFrame();
       frame.setTitle(title);
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       frame.add(this);
       frame.pack();
       frame.setVisible(true);
   }

Here's main. When display is called a parallel thread is launched containing the GUI's listener loop. When the top-level window is closed, then main terminates.

   public static void main(String[] args) {
      ConverterGUI gui = new ConverterGUI();
      gui.display();
   }

The Controller

The ConverterController plays the role of controller. It implements the ActionListener interface. This means two things. First, the controller can register itself with controls that it is interested in:

   celsiusField.addActionListener(controller);
  
farField.addActionListener(controller);

Second, it must implement the actionPerformed method:

public void actionPerformed(ActionEvent e) {
   // interact with model and view here
}

Each time the user types something into one of the text fields then presses the "enter" key, the Java VM creates an instance of ActionEvent containing the identity of the control that fired the event, then passes this object to the actionPerformed method of every registered listener.

Here's the complete implementation:

ConverterController.java

 

An Improvement

We can improve the design of a view by declaring the controllers to be inner classes.

Instances of inner classes (called products) are associated with instances of the outer class that created them (called factories). Products have full access to the fields of their associated factories, private or otherwise. It is not necessary for an "inner" controller to constantly call getter and setter methods to access the controls and models of its outer view class.

Here's an example:

ConverterGUI.java (version 2)

Adding Menus

In the next version of our temperature converter, we add a command menu:

Selecting a command displays an input dialog:

Clicking the "OK" button updates the main window:

Here's the implementation:

ConverterGUI.java

The menu bar is a field:

private JMenuBar menuBar;

Note that the display method attaches the menu bar to the desktop frame:

   public void display() {
       JFrame frame = new JFrame();
       frame.setTitle(title);
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       frame.setJMenuBar(menuBar);
       frame.add(this);
       frame.pack();
       frame.setVisible(true);
   }

The menu bar is initialized in the constructor with a call to makeMenus:

   private void makeMenus() {
      menuBar = new JMenuBar();
      JMenu cmmdMenu = new JMenu("Commands");
      JMenuItem item = new JMenuItem("f2c");
      item.addActionListener(menuController);
      cmmdMenu.add(item);
      item = new JMenuItem("c2f");
      item.addActionListener(menuController);
      cmmdMenu.add(item);
      item = new JMenuItem("exit");
      item.addActionListener(menuController);
      cmmdMenu.add(item);
      menuBar.add(cmmdMenu);
   }

Finally, an inner controller class is declared:

   class MenuController implements ActionListener {
     
      private double getTemperature(String prompt) {
         String response = JOptionPane.showInputDialog(prompt);
         return new Double(response);
      }
      public void actionPerformed(ActionEvent e) {
         String cmmd = e.getActionCommand();
         if (cmmd.equals("f2c")) {
            double fTemp = getTemperature("Enter Fahrenheit temperature");
            double cTemp = model.f2c(fTemp);
            celsiusField.setText("" + cTemp);
            farField.setText("" + fTemp);
         } else if (cmmd.equals("c2f")) {
            double cTemp = getTemperature("Enter Celisius temperature");
            double fTemp = model.c2f(cTemp);
            celsiusField.setText("" + cTemp);
            farField.setText("" + fTemp);
         } else { // cmmd == exit
            System.exit(0);
         }
      }
   }