Skip to main content
Jump to: navigation, search

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)

public class StopWatch implements Runnable {

        /** 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):

  1. public team class WatchUI {
  2.     static int ypos = 150;
  3.  
  4.     /**
  5.      * Role class WatchDisplay is played by the base class StopWatch. The role
  6.      * class WatchDisplay is bound to the base class StopWatch with the keyowrd
  7.      * 'playedBy'.
  8.      */
  9.     protected class WatchDisplay extends JFrame playedBy StopWatch 
  10.     {
  11. 	private JTextField display;
  12. 	private JButton startButton;
  13. 	private JButton stopButton;
  14. 	private JButton clearButton;
  15.  
  16. 	/**
  17. 	 * This constructor is used for automatic role creation. E.g. via
  18. 	 * declared lifting. Role class constructor takes an object of the type
  19. 	 * of the declared base class. Setup the window, create a textfield for
  20. 	 * time display and three buttons "start", "stop", and "clear".
  21. 	 */
  22. 	public WatchDisplay(StopWatch w) {
  23. 		setTitle("Digital Stop Watch");
  24. 		setSize(new Dimension(300, 100));
  25. 		setLocation(410, ypos+=110);
  26. 		Container pane = getContentPane();
  27. 		pane.setLayout(new GridLayout(2,3));
  28. 		pane.add(new JLabel(""));
  29. 		display = new JTextField("0", 8);
  30. 		display.setHorizontalAlignment(JTextField.RIGHT);
  31. 		pane.add(display);
  32. 		pane.add(new JLabel(""));
  33. 		startButton = new JButton("start");
  34. 		startButton.addActionListener(new ActionListener() {
  35. 			public void actionPerformed(ActionEvent e) {
  36. 				start();
  37. 			}});
  38. 		pane.add(startButton);
  39. 		stopButton = new JButton("stop");
  40. 		stopButton.addActionListener(new ActionListener() {
  41. 			public void actionPerformed(ActionEvent e) {
  42. 				stop();
  43. 			}});
  44. 		pane.add(stopButton);
  45. 		clearButton = new JButton("clear");
  46. 		clearButton.addActionListener(new ActionListener() {
  47. 			public void actionPerformed(ActionEvent e) {
  48. 				clear();
  49. 			}});
  50. 		pane.add(clearButton);
  51. 		setVisible(true);
  52. 	}		
  53.  
  54. 	/**
  55. 	 * Shows the new time value on the watch display.
  56. 	 */
  57. 	void update() {
  58. 	    String val = getStringValue();
  59. 	    display.setText(val);
  60. 	}
  61.  
  62. 	// Abstract methods for mapping to the concrete base methods:
  63. 	abstract void   start();
  64. 	abstract void   stop();
  65. 	abstract void   clear();
  66. 	abstract String getStringValue();
  67.  
  68.        // callout method bindings: any call of the abstract WatchDisplay
  69.        // method will be forwarded to the concrete StopWatch method
  70. 	       start            ->       start;
  71. 	       stop             ->       stop;
  72. 	       clear            ->       reset;
  73. 	String getStringValue()	-> int   getValue() 
  74. 	    with {
  75. 		    // result is a predefined name.
  76. 		    result      <-   Integer.toString(result)
  77. 	    }
  78.  
  79. 	/* -------------------------------------------------------------- */
  80.  
  81. 	// Callin method bindings: WatchDisplay (role object) is updated
  82. 	// after the StopWatch (base object) advanced or was reset.
  83. 	   void update()	<- after void advance();
  84. 	   void update()        <- after void reset();
  85.     }
  86.  
  87.     /**
  88.      * The team constructor uses declared lifting. A WatchDisplay role is
  89.      * created for the given StopWatch object.
  90.      */
  91.     public WatchUI (StopWatch as WatchDisplay w) {
  92.     	activate(ALL_THREADS); // Without this, the callin bindings have no effect.
  93.     }
  94. }

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.

Back to the top