TP

Model View Controller Notes

Introduction to Java's Swing Library: StopWatch Program

  • The StopWatch program displays the number of centiseconds since the Start button was pressed.

  • The display updates every centisecond.

  • The Start button resets the timer and disables itself, while enabling the Stop button.

  • The Stop button stops the timer, disables itself, and enables the Start button.

  • All functionality is contained within one file, without using the Model-View-Controller (MVC) pattern.

import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;

public class StopWatch extends JFrame {
 private long startTime = 0;
 private JFrame frame;

 public StopWatch() {
 frame = new JFrame("Digital Stopwatch");
 JPanel buttonPanel = new JPanel();
 JButton startButton = new JButton("Start");
 JButton stopButton = new JButton("Stop");
 JTextField timeField = new JTextField();
 Timer timer = new Timer(10, null);

 buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
 buttonPanel.add(startButton);
 buttonPanel.add(stopButton);
 stopButton.setEnabled(false);

 timer = new Timer(10, e -> timeField.setText(calcTimeFieldText()));

 startButton.addActionListener(new ActionListener() {
 public void actionPerformed(ActionEvent e) {
 startTime = e.getWhen();
 timer.start();
 stopButton.setEnabled(true);
 startButton.setEnabled(false);
 }
 });

 stopButton.addActionListener(new ActionListener() {
 public void actionPerformed(ActionEvent e) {
 timeField.setText(calcTimeFieldText());
 timer.stop();
 stopButton.setEnabled(false);
 startButton.setEnabled(true);
 }
 });

 Container content = frame.getContentPane();
 content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
 timeField.setEditable(false);
 content.add(timeField);
 content.add(buttonPanel);
 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 frame.pack();
 frame.setVisible(true);
 }

 public String calcTimeFieldText() {
 long elapsed = System.currentTimeMillis() - startTime;
 long centisecs = elapsed / 10;
 long seconds = centisecs / 100;
 long minutes = seconds / 60;
 long hours = minutes / 60;

 NumberFormat nf = NumberFormat.getNumberInstance();
 nf.setMinimumIntegerDigits(2);

 String time = "" + nf.format(hours) + ":" + nf.format(minutes % 60) + ":"
 + nf.format(seconds % 60) + "." + nf.format(centisecs % 100);
 return time;
 }

 public static void main(String[] args) {
 new StopWatch();
 }
}
Code for Stopwatch Program
  • Imports necessary Java AWT and Swing libraries:

    • java.awt.Container

    • java.awt.event.ActionEvent

    • java.awt.event.ActionListener

    • java.text.NumberFormat

    • javax.swing.BoxLayout

    • javax.swing.JButton

    • javax.swing.JFrame

    • javax.swing.JPanel

    • javax.swing.JTextField

    • javax.swing.Timer

  • The StopWatch class contains:

    • A JFrame named frame with the title "Digital Stopwatch".

    • A long variable startTime initialized to 0.

StopWatch() Constructor

  • Creates a JPanel called buttonPanel.

  • Creates JButton instances for "Start" and "Stop".

  • Creates a JTextField called timeField.

  • Declares a Timer object.

  • Sets the layout of buttonPanel to BoxLayout.X_AXIS.

  • Adds startButton and stopButton to buttonPanel.

  • Disables stopButton initially using stopButton.setEnabled(false).

  • Creates a Timer that executes every 100 milliseconds (every tenth of a second).

    • The Timer updates timeField with the calculated time using a lambda expression: timefield.setText(calcTimeFieldText());

ActionListeners for Buttons

  • startButton's ActionListener:

    • Sets startTime to the event's time using e.getWhen().

    • Starts the timer.

    • Enables stopButton and disables startButton.

  • stopButton's ActionListener:

    • Updates timeField with the current time.

    • Stops the timer.

    • Disables stopButton and enables startButton.

Setting up the Content Pane

  • Gets the content pane of the frame.

  • Sets the layout of the content pane to BoxLayout.Y_AXIS.

Adding Components and Frame Settings

  • Sets timeField to non-editable using timeField.setEditable(false).

  • Adds timeField and buttonPanel to the content pane.

  • Sets the default close operation for the frame to exit the application using frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE).

  • Packs the frame to fit its contents using frame.pack().

  • Makes the frame visible using frame.setVisible(true).

calcTimeFieldText() Method

  • Calculates the elapsed time in milliseconds: long elapsed = System.currentTimeMillis() - startTime;

  • Converts elapsed time to centiseconds, seconds, minutes, and hours.

    • long centisecs = elapsed/10;

    • long seconds = centisecs/100;

    • long minutes = seconds/60;

    • long hours = minutes/60;

  • Uses NumberFormat to ensure a minimum of two digits for each time unit.

    • NumberFormat nf = NumberFormat.getNumberInstance();

    • nf.setMinimumIntegerDigits(2);

  • Formats the time as a string in the format "HH:MM:SS.CC".

    • String time = "" + nf.format(hours) + ":" + nf.format(minutes%60) + ":" + nf.format(seconds%60) + "." + nf.format(centisecs%100);

main() Method

  • Creates a new instance of the StopWatch class to start the application.

    • new StopWatch();

Class Diagram of StopWatch Program

  • No details provided of Class Diagram

Anonymous Inner Classes vs Lambda Expressions

  • Anonymous Inner Classes:

    • Traditional way of implementing event listeners.

    • Requires creating a new instance of an interface or class.

    • Example:

startButton.addActionListener(new ActionListener() {
 @Override
 public void actionPerformed(ActionEvent e) {
 // code for start button
 }
});

interface ActionListener {
 public void actionPerformed(ActionEvent e);
}
  • Lambda Expressions:

    • More concise syntax for implementing functional interfaces.

    • Reduces boilerplate code.

    • Example:

startButton.addActionListener((ActionEvent e) -> {
 // code for start button
});

Traffic Light Program without MVC

  • The initial Traffic Light program mixes state management and display logic within a single class.

  • If the greenField displays “ON”, the change() method sets greenField to “OFF” and amberField to “ON”.

Problem with Traffic Light Program
  • The code that models the state and the code that displays the state are intertwined.

  • Changing the way lights are displayed (e.g., using colored circles) requires modifying the state-changing logic.

  • This violates the design principle that each class should have only one responsibility.

  • Solution: Separate the state (model) from the display (view).

    • Model: Represents the state.

    • View: Displays the state.

    • When the state changes, the view should not need to change, and vice versa.

The Concept of Model View Controller

Separating Model and View
  • Responsibility of the View:

    • Display the lights.

    • Observe the model and update the lights when the model changes.

  • Responsibility of the Model:

    • Store the state.

    • Change the state when required.

    • Notify the view when the state changes.

  • The model does not depend on the view, allowing for multiple views.

Separating Controller from Model and View
  • Controller:

    • A separate class responsible for converting user inputs (e.g., mouse clicks) into actions that change the model.

    • May choose not to convert certain inputs.

    • Can also modify the view (e.g., disabling buttons).

  • Example:

    • The Run button disables the Change and Initialise buttons and enables the Stop button.

  • Controller Actions:

    • Manipulates the model.

    • Updates the views.

    • Queries the model.

Implementing MVC in Java
  • Uses the Observer pattern.

Observer Pattern
  1. View Subscribes: The view registers with the model by calling addObserver.

  2. Model Publishes: The model notifies its observers (views) of changes by calling setChanged() followed by notifyObservers().

  3. View Requests State: The view retrieves the new state from the model by calling methods like getRed(), getGreen(), etc.

Traffic Light Model

Code and Class Diagram for Traffic Light Model
  • TLModel class extends Observable.

  • Attributes:

    • private boolean red

    • private boolean amber

    • private boolean green

  • Getter methods to access the state:

    • public boolean getRed()

    • public boolean getAmber()

    • public boolean getGreen()

change() Method
  • Implements the traffic light state transition logic.

  • State transitions:

    • Red -> Amber

    • Red & Amber -> Green

    • Green -> Amber

    • Otherwise -> Red

  • Notifies observers after the state change:

    • setChanged();

    • notifyObservers();

initialise() Method
  • Initializes the traffic light to the red state.

    • red = true;

    • amber = false;

    • green = false;

  • Notifies observers after initialization.

TLModel() Constructor
  • Calls the initialise() method to set the initial state.

Traffic Light View

Code for Traffic Light View
  • TLView class implements the Observer interface.

  • Attributes:

    • private static final Dimension PANEL_SIZE = new Dimension(200,200);

    • private TLModel model

    • private TLController controller

    • private JFrame frame

    • private JPanel panel

    • private JTextField redField, amberField, greenField

    • private JLabel redLabel, amberLabel, greenLabel

    • private JButton changeButton, initialiseButton

TLView() Constructor
  • Takes a TLModel and TLController as arguments.

  • Sets the model and controller.

  • Registers the view as an observer of the model: model.addObserver(this);

  • Calls createControls() to initialize the UI components.

  • Sets the view for the controller using controller.setView(this);

  • Calls update(model, null) to set the initial display.

createControls() Method
  • Creates the main JFrame.

    • Sets the default close operation to exit the application.

  • Gets the content pane and sets its layout to BoxLayout.X_AXIS.

  • Calls createPanel() to create the panel with the traffic light components.

  • Adds the panel to the content pane.

  • Packs the frame and sets it to non-resizable.

  • Makes the frame visible.

createPanel() Method
  • Creates a JPanel and sets its layout to GridLayout(4,2).

  • Sets the text fields to non-editable.

  • Adds labels and text fields for red, amber, and green lights.

  • Creates and adds changeButton and initialiseButton with their respective action listeners that call the appropriate controller methods.

  • Sets the preferred size of the panel using panel.setPreferredSize(PANEL_SIZE);

update() Method
  • Implements the update method from the Observer interface.

  • Updates the text fields based on the model's state