SDSU CS 596 Java Programming
Fall Semester, 1998
Model-View
To Lecture Notes Index
© 1998, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 21-Dec-98

Contents of Doc 28, Model-View


References


Design Patterns: Elements of Reusable Object-Oriented Software , Gamma, Helm, Johnson, Vlissides, pages 293-304

Listen Here!S-nov23 48secs, Q-nov24 48secs Doc 28, Model-View Slide # 2

Separating the GUI from the Program

A Simple Example

Problem: create a simple counter that can:

Increase
Decrease
Be reset to zero

Display the counter in a window and allow the user to increase or decrease the counter via buttons.

Listen Here!S-nov23 1min, Q-nov24 3mins Doc 28, Model-View Slide # 3
A Poor Solution - The Window is the Counter!

import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
class  BadCounterExample  extends Frame  {
   Button increase = new Button( "Increase" );
   Button decrease = new Button( "Decrease" );
   Button reset = new Button( "Reset" );
   int count = 0;
      
   public BadCounterExample( int  width, int height ) {
      setTitle( "Bad Counter Example" );
      setSize( width, height );
      setLayout( new FlowLayout() );
      add( increase );
      add( decrease );
      add( reset );
      
      increase.addActionListener( new IncreaseListener() );
      decrease.addActionListener( new DecreaseListener() );
      reset.addActionListener( new ResetListener() );
      show();
   }
   public  void  paint(  Graphics  display  )  {
      display.drawString(  "The count is " + count , 50, 50  );
   }
   public  static  void  main( String  args[] ){
      new  BadCounterExample( 200,  100 );
   }
   class IncreaseListener implements ActionListener {
      public void actionPerformed( ActionEvent event )  {
         count++;
         repaint();
      }
   }
   class DecreaseListener implements ActionListener {
      public void actionPerformed( ActionEvent event )  {
         count--;
         repaint();
      }
   }
   class ResetListener implements ActionListener {
      public void actionPerformed( ActionEvent event )  {
         count = 0;
         repaint();
      }
   }
}

Listen Here!S-nov23 2mins Doc 28, Model-View Slide # 4
Output After clicking on Increase 6 times


What is Wrong with the about program?It works!
What happens if we need to run the program in batch mode? There is no way to separate the code from window environment!

Listen Here!S-nov23 1min, Q-nov24 1min Doc 28, Model-View Slide # 5

Model-View


A Model is a system of objects that perform a task

A View:

Displays a representation of the model by asking model for values to display

Handles all interaction with user about the model

Keeping the model and view separate

Give flexibility in combining new views to a model
Allows multiple views on the same model


Listen Here!S-nov23 34secs, Q-nov24 27secs Doc 28, Model-View Slide # 6
Counter Model
We use a separate class to implement the counter model. Of course, normally the model is more complex than the counter. Hence, it may require a lot more that one class to implement.
class Counter{
   private int count = 0;
   
   public void increase()  { 
      count++; 
   }
   public void decrease() { 
      count--; 
   }
   public void reset() { 
      count = 0; 
   }
      
   public int value() { 
      return count; 
   }
   public String toString() { 
      return String.valueOf( count ); 
   }
}

Listen Here!S-nov23 47secs, Q-nov24 51secs Doc 28, Model-View Slide # 7
A View for the Model

import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
class  CounterExample  extends Frame  {
   Button increase = new Button( "Increase" );
   Button decrease = new Button( "Decrease" );
   Button reset = new Button( "Reset" );
   Counter count;
      
   public CounterExample( int  width, int height, Counter outside ) {
      setTitle( "Model-View Example" );
      setSize( width, height );
      setLayout( new FlowLayout() );
      add( increase );
      add( decrease );
      add( reset );
      count = outside;
      increase.addActionListener( new IncreaseListener() );
      decrease.addActionListener( new DecreaseListener() );
      reset.addActionListener( new ResetListener() );
      show();
   }
   public  void  paint(  Graphics  display  )  {
      display.drawString(  "The count is " + count , 50, 50  );
   }
   class IncreaseListener implements ActionListener {
      public void actionPerformed( ActionEvent event )  {
         count.increase();
         repaint();
      }
   }
   class DecreaseListener implements ActionListener {
      public void actionPerformed( ActionEvent event )  {
         count.decrease();
         repaint();
      }
   }
   class ResetListener implements ActionListener {
      public void actionPerformed( ActionEvent event )  {
         count.reset();
         repaint();
      }
   }
}

Listen Here!S-nov23 37secs Doc 28, Model-View Slide # 8
Driver ProgramTwo Views Two Models

class ButtonTest
   {
   public  static  void  main( String  args[] )
      {
      Counter test = new Counter();
      new  CounterExample( 200,  100, test );
      test = new Counter();
      new  CounterExample( 200,  100, test );
      }
   }


Listen Here!S-nov23 2mins, Q-nov24 2mins Doc 28, Model-View Slide # 9
How about Two Views, One Model?

class ButtonTest
   {
   public  static  void  main( String  args[] )
      {
      Counter test = new Counter();
      CounterExample ViewA = new  CounterExample( 200,  100, test );
      CounterExample ViewB = new CounterExample( 200,  100, test );
      }
   }


What Went Wrong?


When view A changes the counter how does view B know to update?

Who's responsibility is it to notify B of the change?

Listen Here!S-nov23 1min, Q-nov24 2mins Doc 28, Model-View Slide # 10

Observable and Observer

(Dependents)(Publish-Subscribe)
The Observer is similar to the listeners we have seen before. The Observable is similar to the event sources for the listeners. The difference is in the functionality of the Observable class. Its event broadcast mechanism is more complex.

Class java.util.Observable

Observable object may have any number of Observers. Whenever the Observable instance changes, it notifies all of its observers. Notification is a two step process. First, the Observable calls its setChanged() method to set the changed flag to true. Second, the Observable calls its update() method. If the Observable's changed flag is true, then the update method notifies on all observers and sets the flag to false. The two step process, allows multiple and independent changes to use one update call to notify the observers. It also simplifies the process of rolling back complex changes before reaching the commit point. If changes are rolled back, a call to clearChange() sets the changed flag to false. Any call to update() will then not notify the observers.

Interface java.util.Observer

When implemented, this interface allows all classes to be observable by instances of class Observer


Listen Here!S-nov23 5mins, Q-nov24 6mins, Q-nov24 1min Doc 28, Model-View Slide # 11
java.util.Observable Methods

addObserver(Observer)
Adds an observer to the observer list.
clearChanged()
Clears an observable change.
countObservers()
Counts the number of observers.
deleteObserver(Observer)
Deletes an observer from the observer list.
deleteObservers()
Deletes observers from the observer list.
hasChanged()
Returns a true boolean if an observable change has occurred.
notifyObservers()
Notifies all observers if an observable change occurs.
notifyObservers(Object)
Notifies all observers of the specified observable change which occurred. Object is passed to each observer.
setChanged()
Sets a flag to note an observable change.


Interface java.util.Observer
update
Called when observers in the observable list need to be updated


Listen Here!S-nov23 2mins, Q-nov24 2mins Doc 28, Model-View Slide # 12
Simple Example
class SimpleCounter extends Observable{
   public int count = 0;
   
   public void increase() { 
      count++; 
      setChanged();
      notifyObservers(); 
   }
}
   
class SimpleObserver implements Observer  {
   String id;
   public SimpleObserver( String id ) {
      this.id = id ;
   }
   public void update( Observable sender, Object message ) {
      System.out.println( "From " + id + " New value " + 
         ((SimpleCounter) sender).count );
   }
}
   
class ObserveTest {
   public  static  void  main( String  args[] ) {
      SimpleCounter test = new SimpleCounter();
      SimpleObserver a = new SimpleObserver( "a" );
      test.addObserver( a);
      test.addObserver( new SimpleObserver( "b" ) );
      test.increase();
      test.addObserver( new SimpleObserver( "c" ) );
      test.increase();
      }
   }

Listen Here!S-nov23 2mins, Q-nov24 2mins Doc 28, Model-View Slide # 13
Running the Example

class ObserveTest {
   public  static  void  main( String  args[] ) {
      SimpleCounter test = new SimpleCounter();
      SimpleObserver a = new SimpleObserver( "a" );
      test.addObserver( a);
      test.addObserver( new SimpleObserver( "b" ) );
      test.increase();
      test.addObserver( new SimpleObserver( "c" ) );
      test.increase();
      }
   }
Output
From b New value 1
From a New value 1
From c New value 2
From b New value 2
From a New value 2

Doc 28, Model-View Slide # 14
Adding the Same Observer Twice?

class ObserveTest {
   public  static  void  main( String  args[] ) {
      SimpleCounter test = new SimpleCounter();
      SimpleObserver a = new SimpleObserver( "a" );
      test.addObserver( a);
      test.addObserver( a );
      test.increase();
      System.out.println( "After first increase");
      test.addObserver( a );
      test.increase();
      }
   }
Output
From a New value 1
After first increase 
From a New value 2

Doc 28, Model-View Slide # 15
Deleting Observers

class ObserveTest {
   public  static  void  main( String  args[] ) {
      SimpleCounter test = new SimpleCounter();
      SimpleObserver a = new SimpleObserver( "a" );
      SimpleObserver b = new SimpleObserver( "b" );
      test.addObserver( a);
      test.addObserver( b);
      test.increase();
      System.out.println( "Delete a" );
      test.deleteObserver( a );
      test.increase();
      System.out.println( "Delete a again" );
      test.deleteObserver( a );
      test.increase();
      System.out.println( "Delete all" );
      test.deleteObservers( );
      test.increase();
      }
   }
Output
From b New value 1
From a New value 1
Delete a
From b New value 2
Delete a again
From b New value 3
Delete all

Listen Here!S-nov23 1min, Q-nov24 41secs Doc 28, Model-View Slide # 16

Counter Example

We will go through the complete counter example with the observable-observer system. Here is the full counter modified as an Observable (Model).

class Counter extends Observable {
   private int count = 0;
   
   public void increase()  { 
      count++; 
      setChanged();
      notifyObservers(); 
   }
   public void decrease() { 
      count--; 
      setChanged();
      notifyObservers(); 
   }
   public void reset() { 
      count = 0; 
      setChanged();
      notifyObservers(); 
   }
      
   public int value() { 
      return count; 
   }
   public String toString() { 
      return String.valueOf( count ); 
   }
}

Listen Here!S-nov23 48secs, Q-nov24 1min Doc 28, Model-View Slide # 17
A Counter Observer View
import java.awt.*;      import java.awt.event.*;
import java.util.*;
class  CounterExample  extends Frame implements Observer {
   Button increase = new Button( "Increase" );
   Button decrease = new Button( "Decrease" );
   Button reset = new Button( "Reset" );
   Counter count;
   public CounterExample( int  width, int height, Counter outside ) {
      setTitle( "Observer Example" );
      setSize( width, height );
      setLayout( new FlowLayout() );
      add( increase );
      add( decrease );
      add( reset );
      
      count = outside;
      count.addObserver( this );
      
      increase.addActionListener( new IncreaseListener() );
      decrease.addActionListener( new DecreaseListener() );
      reset.addActionListener( new ResetListener() );
      show();
   }
   public void update( Observable sender, Object message ) {
      repaint();
   }
   public  void  paint(  Graphics  display  )  {
      display.drawString(  "The count is " + count , 50, 50  );
   }
   class IncreaseListener implements ActionListener {
      public void actionPerformed( ActionEvent event )  {
         count.increase();
      }
   }
   class DecreaseListener implements ActionListener {
      public void actionPerformed( ActionEvent event )  {
         count.decrease();
      }
   }
   class ResetListener implements ActionListener {
      public void actionPerformed( ActionEvent event )  {
         count.reset();
      }
   }
}

Listen Here!S-nov23 4mins, Q-nov24 5mins Doc 28, Model-View Slide # 18
Running the Example
class ModelViewTest{
   public  static  void  main( String  args[] ){
      Counter test = new Counter();
      CounterExample ViewA = new  CounterExample( 200,  100, test );
      CounterExample ViewB = new CounterExample( 200,  100, test );
   }
}

Interaction Diagram

This diagram shows the method calls between ViewA, test and ViewB when the increase button of ViewA is pressed. Time progresses as you read from top to bottom.


Copyright © 1998 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
All rights reserved.

visitors since 22-Nov-98