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.Containerjava.awt.event.ActionEventjava.awt.event.ActionListenerjava.text.NumberFormatjavax.swing.BoxLayoutjavax.swing.JButtonjavax.swing.JFramejavax.swing.JPaneljavax.swing.JTextFieldjavax.swing.Timer
The
StopWatchclass contains:A
JFramenamedframewith the title "Digital Stopwatch".A
longvariablestartTimeinitialized to 0.
StopWatch() Constructor
Creates a
JPanelcalledbuttonPanel.Creates
JButtoninstances for "Start" and "Stop".Creates a
JTextFieldcalledtimeField.Declares a
Timerobject.Sets the layout of
buttonPaneltoBoxLayout.X_AXIS.Adds
startButtonandstopButtontobuttonPanel.Disables
stopButtoninitially usingstopButton.setEnabled(false).Creates a
Timerthat executes every 100 milliseconds (every tenth of a second).The
TimerupdatestimeFieldwith the calculated time using a lambda expression:timefield.setText(calcTimeFieldText());
ActionListeners for Buttons
startButton'sActionListener:Sets
startTimeto the event's time usinge.getWhen().Starts the
timer.Enables
stopButtonand disablesstartButton.
stopButton'sActionListener:Updates
timeFieldwith the current time.Stops the
timer.Disables
stopButtonand enablesstartButton.
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
timeFieldto non-editable usingtimeField.setEditable(false).Adds
timeFieldandbuttonPanelto 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
NumberFormatto 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
StopWatchclass 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
greenFielddisplays “ON”, thechange()method setsgreenFieldto “OFF” andamberFieldto “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
View Subscribes: The view registers with the model by calling
addObserver.Model Publishes: The model notifies its observers (views) of changes by calling
setChanged()followed bynotifyObservers().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
TLModelclass extendsObservable.Attributes:
private boolean redprivate boolean amberprivate 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
TLViewclass implements theObserverinterface.Attributes:
private static final Dimension PANEL_SIZE = new Dimension(200,200);private TLModel modelprivate TLController controllerprivate JFrame frameprivate JPanel panelprivate JTextField redField,amberField,greenFieldprivate JLabel redLabel,amberLabel,greenLabelprivate JButton changeButton,initialiseButton
TLView() Constructor
Takes a
TLModelandTLControlleras 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
JPaneland sets its layout toGridLayout(4,2).Sets the text fields to non-editable.
Adds labels and text fields for red, amber, and green lights.
Creates and adds
changeButtonandinitialiseButtonwith 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
updatemethod from theObserverinterface.Updates the text fields based on the model's state