Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
OTExample Stopwatch
In a running OTDT you can easily import this project into your workspace using this menu path:
New > Example > Object Teams > Stop Watch Example
The Stopwatch example shows the most simple Model View Controller architecture using OT/J. It contains basically of two classes:
Stopwatch (Model)
/** Current time value */
private int time = 0;
/** Flag that indicates if the watch is running */
private boolean running;
/** Flag that indicates if there is a stop request */
private boolean stopRequest;
/** Increases the time value. */
private synchronized void advance() {
time++;
}
/** Starts the watch thread. */
public synchronized void start() {
if (!running) {
setRunning(true);
setStopped(false);
(new Thread(this)).start();
}
}
/** Stops the watch thread. */
public synchronized void stop() {
setStopped(true);
}
/** Resets the time value. */
public synchronized void reset() {
time = 0;
}
/** Returns the current time value. */
public synchronized int getValue() {
return time;
}
/** Sets the running flag value. */
private synchronized void setRunning(boolean value) {
running = value;
}
/** Returns the stop flag value */
private synchronized boolean isStopped() {
return stopRequest;
}
/** Sets the stop flag value */
private synchronized void setStopped(boolean value) {
stopRequest = value;
}
/** This method contains the main loop that is executed while the watch is running. */
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
setRunning(false);
return;
}
if (isStopped()) {
setRunning(false);
return;
}
advance();
}
}
}
This pure Java class only implements the functionality of a stopwatch with no thought whatsoever about display of any kind.
Now for class WatchUI (combined View and Controller):
public team class WatchUI {
static int ypos = 150;
/**
* Role class WatchDisplay is played by the base class StopWatch. The role
* class WatchDisplay is bound to the base class StopWatch with the keyowrd
* 'playedBy'.
*/
protected class WatchDisplay extends JFrame playedBy StopWatch
{
private JTextField display;
private JButton startButton;
private JButton stopButton;
private JButton clearButton;
/**
* This constructor is used for automatic role creation. E.g. via
* declared lifting. Role class constructor takes an object of the type
* of the declared base class. Setup the window, create a textfield for
* time display and three buttons "start", "stop", and "clear".
*/
public WatchDisplay(StopWatch w) {
setTitle("Digital Stop Watch");
setSize(new Dimension(300, 100));
setLocation(410, ypos+=110);
Container pane = getContentPane();
pane.setLayout(new GridLayout(2,3));
pane.add(new JLabel(""));
display = new JTextField("0", 8);
display.setHorizontalAlignment(JTextField.RIGHT);
pane.add(display);
pane.add(new JLabel(""));
startButton = new JButton("start");
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
start();
}});
pane.add(startButton);
stopButton = new JButton("stop");
stopButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
stop();
}});
pane.add(stopButton);
clearButton = new JButton("clear");
clearButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
clear();
}});
pane.add(clearButton);
setVisible(true);
}
/**
* Shows the new time value on the watch display.
*/
void update() {
String val = getStringValue();
display.setText(val);
}
// Abstract methods for mapping to the concrete base methods:
abstract void start();
abstract void stop();
abstract void clear();
abstract String getStringValue();
// callout method bindings: any call of the abstract WatchDisplay
// method will be forwarded to the concrete StopWatch method
start -> start;
stop -> stop;
clear -> reset;
String getStringValue() -> int getValue()
with {
// result is a predefined name.
result <- Integer.toString(result)
}
/* -------------------------------------------------------------- */
// Callin method bindings: WatchDisplay (role object) is updated
// after the StopWatch (base object) advanced or was reset.
void update() <- after void advance();
void update() <- after void reset();
}
/**
* The team constructor uses declared lifting. A WatchDisplay role is
* created for the given StopWatch object.
*/
public WatchUI (StopWatch as WatchDisplay w) {
activate(ALL_THREADS); // Without this, the callin bindings have no effect.
}
}
What you see in this class
- It's a team class (l.1)
- It declares a role WatchDisplay extends JFrame playedBy StopWatch (l.9)
- The role's constructor uses normal Swing programming to create a little UI (ll.22-52)
- The buttons in this UI are connected to methods start,stop,clear (ll.36,42,48)
- These methods are declared abstractly (ll.63-65)
- A little update method sets the display text to whatever the abstract method getStringValue returns (ll.57-60,66)
- The UI is connected to the Model only be a set of method bindings:
- callout method bindings make corresponding methods of StopWatch accessible within WatchDisplay (ll.68-77)
- thanks to these bindings these methods are no longer abstract
- clear and getStringValue are mapped to method with different names in the base class (ll.72,73)
- the parameter mapping for getStringValue additionally converts the provided int into a String (ll.74-77)
- callin method bindings set the triggers when the display should be updated (ll.81-84)
- The team's constructor receives a base object (StopWatch) which is automatically lifted to its WatchDisplay role before method entry (keyword as) (l.91)
- By lifting the lifting constructor (l.22) is invoked which puts up the UI
- The constructor furthermore activates the team so that it indeed receives triggers via its callin bindings.
The program is starting using this tiny Main:
public class Main { public static void main(String[] args) { StopWatch w = new StopWatch(); new WatchUI(w); } }
We create both a model and a view, passing the model to the view where it will be decorated with a role.
The version shipped with the OTDT additionally bring an AnalogWatch in order to show how different views are connected to the same model. However, no further OT/J constructs or patterns are used for this.