Java: Presentation-Model Structure

Previous - Big Blob, Next - MVC

The single most important structure in a program is to separate the user interface from the model (business logic, domain, ...). This provides a huge improvement in simplicity, altho at first it may not appear so. Program enhancements and maintenance are much simpler with this structure.

A further separation can be found in the next example which separates the View (displaying information) from the Controller (processing user interactions) -- the MVC pattern. However, for most applications, you will probably get most of the benefit without some of the complications by simply sticking to the Presentation-Model organization that is shown here.

Main class

  1 
  2 
  3 
  4 
  5 
  6 
  7 
  8 
  9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
// structure/presentation-model/CalcV3.java 
//   Calculator with separation of presentation and model.
// Fred Swartz -- December 2004

// Program Organization: Separate View+Controller and Model

import javax.swing.*;

public class CalcV3 {
    public static void main(String[] args) {     
        JFrame presentation = new CalcViewController();
        presentation.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        presentation.setTitle("Simple Calc - Presentation-Model");
        presentation.setVisible(true);
    }
}

The Presentation (GUI, View+Controller, ...)

  1 
  2 
  3 
  4 
  5 
  6 
  7 
  8 
  9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
 60 
 61 
 62 
 63 
 64 
 65 
 66 
 67 
 68 
 69 
 70 
 71 
 72 
 73 
 74 
 75 
 76 
 77 
 78 
 79 
 80 
 81 
 82 
 83 
 84 
 85 
 86 
 87 
 88 
 89 
 90 
 91 
 92 
// structure/presentation-model/CalcViewController.java
// Fred Swartz - December 2004

// GUI Organization - GUI independent of model.
// 
//     GUI subclasses JFrame and builds it in the constructor.
//
//     The GUI creates a calculator model object, but knows nothing 
//     about the internal implementation of the calculator.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class CalcViewController extends JFrame {
    //... Constants
    private static final String INITIAL_VALUE = "1";
    
    //... The Model.
    private CalcModel  m_logic;
    
    //... Components
    private JTextField m_userInputTf = new JTextField(5);
    private JTextField m_totalTf     = new JTextField(20);
    private JButton    m_multiplyBtn = new JButton("Multiply");
    private JButton    m_clearBtn    = new JButton("Clear");
    
    /** Constructor */
    CalcViewController() {
        //... Set up the logic
        m_logic = new CalcModel();
        m_logic.setValue(INITIAL_VALUE);
        
        //... Initialize components
        m_totalTf.setText(m_logic.getValue());
        m_totalTf.setEditable(false);
        
        //... Layout the components.        
        JPanel content = new JPanel();
        content.setLayout(new FlowLayout());
        content.add(new JLabel("Input"));
        content.add(m_userInputTf);
        content.add(m_multiplyBtn);
        content.add(new JLabel("Total"));
        content.add(m_totalTf);
        content.add(m_clearBtn);
        
        //... finalize layout
        this.setContentPane(content);
        this.pack();
        
        //... Set button listeners.
        m_multiplyBtn.addActionListener(new MultiplyListener());
        m_clearBtn.addActionListener(new ClearListener());
    }//end constructor
    
    
    ////////////////////////////////////////// inner class MultiplyListener
    /** When a mulitplication is requested.
     *  1. Get the user input number.
     *  2. Call the model to mulitply by this number.
     *  3. Get the result from the Model.
     *  4. Set the Total textfield to this result.
     * If there was an error, display it in a JOptionPane.
     */
    class MultiplyListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            String userInput = "";
            try {
                userInput = m_userInputTf.getText();
                m_logic.multiplyBy(userInput);
                m_totalTf.setText(m_logic.getValue());
            } catch (NumberFormatException nfex) {
                JOptionPane.showMessageDialog(CalcViewController.this, 
                                      "Bad input: '" + userInput + "'");
            }
        }
    }//end inner class MultiplyListener
    
    
    //////////////////////////////////////////// inner class ClearListener
    /**  1. Reset model.
     *   2. Put model's value into Total textfield.
     */    
    class ClearListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            m_logic.reset();
            m_totalTf.setText(m_logic.getValue());
        }
    }// end inner class ClearListener

}

The Model (Logic, ...)

  1 
  2 
  3 
  4 
  5 
  6 
  7 
  8 
  9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
// structure/presentation-model/CalcModel.java
// Fred Swartz - December 2004
// Model
//     This model is completely independent of the user interface.
//     It could as easily be used by a command line or web interface.

import java.math.BigInteger;

public class CalcModel {
    //... Constants
    private static final String INITIAL_VALUE = "0";
    
    //... Member variable defining state of calculator.
    private BigInteger m_total;  // The total current value state.
    
    //========================================================== constructor
    /** Constructor */
    CalcModel() {
        reset();
    }
    
    //================================================================ reset
    /** Reset to initial value. */
    public void reset() {
        m_total = new BigInteger(INITIAL_VALUE);
    }
    
    //=========================================================== multiplyBy
    /** Multiply current total by a number.
    *@param operand Number (as string) to multiply total by.
    */
    public void multiplyBy(String operand) {
        m_total = m_total.multiply(new BigInteger(operand));
    }
    
    //============================================================= setValue
    /** Set the total value. 
    *@param value New value that should be used for the calculator total. 
    */
    public void setValue(String value) {
        m_total = new BigInteger(value);
    }
    
    //============================================================= getValue
    /** Return current calculator total. */
    public String getValue() {
        return m_total.toString();
    }
}