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
namedframe
with the title "Digital Stopwatch".A
long
variablestartTime
initialized to 0.
StopWatch() Constructor
Creates a
JPanel
calledbuttonPanel
.Creates
JButton
instances for "Start" and "Stop".Creates a
JTextField
calledtimeField
.Declares a
Timer
object.Sets the layout of
buttonPanel
toBoxLayout.X_AXIS
.Adds
startButton
andstopButton
tobuttonPanel
.Disables
stopButton
initially usingstopButton.setEnabled(false)
.Creates a
Timer
that executes every 100 milliseconds (every tenth of a second).The
Timer
updatestimeField
with the calculated time using a lambda expression:timefield.setText(calcTimeFieldText());
ActionListeners for Buttons
startButton
'sActionListener
:Sets
startTime
to the event's time usinge.getWhen()
.Starts the
timer
.Enables
stopButton
and disablesstartButton
.
stopButton
'sActionListener
:Updates
timeField
with the current time.Stops the
timer
.Disables
stopButton
and 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
timeField
to non-editable usingtimeField.setEditable(false)
.Adds
timeField
andbuttonPanel
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”, thechange()
method setsgreenField
to “OFF” andamberField
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
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
TLModel
class extendsObservable
.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 theObserver
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
andTLController
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 toGridLayout(4,2)
.Sets the text fields to non-editable.
Adds labels and text fields for red, amber, and green lights.
Creates and adds
changeButton
andinitialiseButton
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 theObserver
interface.Updates the text fields based on the model's state