SDSU CS 696: Advanced OO
Spring Semester, 1997
Doc 18 Observer

To Lecture Notes Index
San Diego State University -- This page last updated Apr 24, 1997

Contents of Doc 18 Observer


Observer slide # 1
...Java Example slide # 1
......The Classes slide # 2
......Class Descriptions slide # 3
......class Counter slide # 5
......class IncreaseDetector slide # 6
......abstract class CounterButton slide # 7
......class IncreaseButton slide # 8
......class DecreaseButton slide # 8
......class CounterView slide # 9
......class CounterTextView slide # 10
......class ButtonController slide # 11
......class RectangleView slide # 12
......Sample Program slide # 14
......Runtime Object Interaction slide # 15
...Implementation Issues slide # 16
......Mapping subjects(Observables) to observers slide # 16
......Observing more than one subject slide # 16
......Who Triggers the update? slide # 17
......Make sure Subject is self consistent before Notification slide # 18
......Adding information about the change slide # 20



Doc 18 Observer Slide # 1

Observer


Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically

Use the Observer pattern:

Java Example


Java API implements a framework for this pattern

Java's observer interface is the Observer abstract class in the pattern

Java's Observable class is the Subject abstract class in the pattern


Doc 18 Observer Slide # 2
An Observer/Observable Example

The Classes










indicates interface

Doc 18 Observer Slide # 3

Class Descriptions




Counter
A Counter can increase or decrease by one. Each time a counter changes value, it notifies its observers of the type of change.

IncreaseDectector
IncreaseDetector is an observer that observes counters.
IncreaseDetector counts the number of times one of its observables increases. It notifies its observers when it changes



CounterButton
Abstract class for changing a counter each time the button is pressed

IncreaseButton
A button that increases a counter each time the button is pressed
DecreaseButton
A button that decreases a counter each time the button is pressed
Doc 18 Observer Slide # 4
Class Descriptions



CounterView
A parent window view that observes a counter. Does nothing with counter. Used as parent for other views.


CounterTextView
A window for displaying the value of a counter in ASCII.


ButtonController
A window for changing the value of a counter using IncreaseButton and DecreaseButton.


RectangleView
Draws a colored rectangle that depends on two counters. One counter is the width of the rectangle, the other counter is the height of the rectangle. The color of rectangle varies with its shape.


Doc 18 Observer Slide # 5

class Counter

/**
 * A counter can increase or decrease by one. Each time a counter 
 * changes value, it notifies its observers of the type of change. 
 */

class Counter extends Observable 
     {
     public static final String INCREASE = "increase";
     public static final String DECREASE = "decrease";

     private int count = 0;
     private String label;
     
     public Counter( String label )     {     this.label = label; }
          
     public String label()               {  return label; }
     public int value()                     {  return count; }
     public String toString()           {  return String.valueOf( count );}
          
     public void increase() 
          { 
          count++; 
          setChanged();
          notifyObservers( INCREASE ); 
          }
               
     public void decrease() 
          { 
          count--;
          setChanged();
          notifyObservers( DECREASE ); 
          }     
     }

Doc 18 Observer Slide # 6

class IncreaseDetector


/**
 * IncreaseDetector is an observer that observes counters. 
 * IncreaseDetector counts the number of times one of its observables 
 * increases.
 */

class IncreaseDetector extends Counter implements Observer
     {
     public IncreaseDetector( String label )
          {
          super( label );
          }
          
     public void update( Observable whatChanged, Object message)
          {
          if ( message.equals( Counter.INCREASE) )
               increase();
          }
     }


Doc 18 Observer Slide # 7

abstract class CounterButton


/**
 * An abstract class for changing a counter each time the button is 
 * pressed
 */

abstract class CounterButton extends Button
     {
     protected Counter count;
     
     public CounterButton( String buttonName, Counter count )
          {
          super( buttonName );
          this.count = count;
          }
          
     public boolean action( Event processNow, Object argument )
          {
          changeCounter();
          return true;
          }
     
     abstract protected void changeCounter();
     }


Doc 18 Observer Slide # 8

class IncreaseButton

/**
 * A button that increases a counter each time the button is pressed
 */

class IncreaseButton extends CounterButton
     {
     public IncreaseButton( Counter count )
          { 
          super( "Increase", count ); 
          }

     protected void changeCounter()
          { 
          count.increase(); 
          }     
     }

class DecreaseButton

/**
 * A button that decreases a counter each time the button is pressed
 */

class DecreaseButton extends CounterButton
     {
     public DecreaseButton( Counter count )
          {
          super( "Decrease", count );
          }
          
     protected void changeCounter()
          { 
          count.decrease();
          }
     }

Doc 18 Observer Slide # 9

class CounterView

/**
 * A parent window view that observes a counter
 */

class CounterView extends Frame implements Observer
     {
     public CounterView( String label, int x, int y, int width, int
height )
          {
          setTitle( label );
          reshape(x, y,  width, height );
          setLayout( new FlowLayout() );
          }
     
     /**
      * Redraw the window when an observed counter changes
      */     

     public void update(Observable counter, Object argument)
          {
          repaint();
          }
     }



Doc 18 Observer Slide # 10

class CounterTextView


/**
 * A window for displaying the value of a counter in ascii
 */

class CounterTextView extends CounterView 
     {
     Counter model;
     
     public CounterTextView( Counter model, String label, 
                                        int x, int y, int width, int height )
          {
          super( label, x, y, width, height );
          this.model = model;
          model.addObserver( this );
          
          show();
          }
          
     public  void  paint(  Graphics  display  )  
          {
          int y = bounds().height - 20;
          int x = 20;
          display.drawString(  "The value of " + model.label() +
                                    " is " + model , x, y  );
          }

}



Doc 18 Observer Slide # 11

class ButtonController

/**
 * A window for changing the value of a counter
 */

class  ButtonController extends CounterView 
     {     
     public ButtonController( Counter model, int x, int y, 
                                        int width, int height )
          {
          super(  model.label(), x, y, width, height );
          model.addObserver( this );
          
          // show the value of the counter
          new CounterTextView( model, "Value of " + model.label(),
                                         x + width + 5,y, 150, 50);

          // buttons to change counter
          add( new IncreaseButton( model ));
          add( new DecreaseButton( model ));
          show();
          }
     }


Doc 18 Observer Slide # 12

class RectangleView

/**
 * Draws a colored rectangle that depends on two counters. One 
 * counter is the width of the rectangle, the other counter is the
 * height of the rectangle. The color of rectangle varies with its shape 
 */

class RectangleView  extends CounterView
     {
     Counter width;
     Counter height;
     
     public RectangleView( Counter rectWidth,  Counter rectHeight, 
                                        int x, int y )
          {
          super( "Rectangle", x, y, 150, 150 );
          height =  rectHeight;
          width = rectWidth;
          rectWidth.addObserver( this );
          rectHeight.addObserver( this );
          show();
          }


Doc 18 Observer Slide # 13
class RectangleView Continued

     public  void  paint(  Graphics  display  )  
          {
          int x = 10;
          int y = 10;
          
          // Magnify value by 5 to get a bigger visual effect
          int height =  5 * this.height.value();
          int width = 5 * this.width.value();
          
          // Determine color. Colors chosen for fun.
          // The factor of 3 is just to magnify effect of change
          if (( width >= 0 ) && ( height >= 0 ))
               display.setColor( new Color( 3*width, 3*height, 
                                                  width + height) );
          else if (( width < 0 ) && ( height >= 0 ))
               display.setColor( Color.pink );
          else if (( width >= 0 ) && ( height < 0 ))
               display.setColor( Color.orange );
          else if (( width < 0 ) && ( height < 0 ))
               display.setColor( Color.red );
          
          display.fillRect(x, y, Math.abs(width), Math.abs( height ) );
          }
     }




Doc 18 Observer Slide # 14

Sample Program


class TestButton
     {
     public  static  void  main( String  args[] ){
          Counter x = new Counter( "x" );
          Counter y = new Counter( "y" );

          IncreaseDetector plus = new IncreaseDetector( "Pluses" );
          x.addObserver( plus ); 
          y.addObserver( plus ); 

          new ButtonControler( x,  30, 30, 150, 50 );
          new ButtonControler( y,  30, 100, 150, 50 );

          new CounterTextView( plus, "# of increases", 30, 170, 150, 50);
          new RectangleView( x, y, 340, 30 );
          }
     }


Doc 18 Observer Slide # 15

Runtime Object Interaction






Doc 18 Observer Slide # 16

Implementation Issues


Mapping subjects(Observables) to observers


Use list in subject
Use hash table

Observing more than one subject

If an observer has more than one subject how does it know which one changed?

Pass information in the update method

Dangling references to Deleted Subjects


Doc 18 Observer Slide # 17

Who Triggers the update?


class Counter extends Observable 
{// some code removed
public void increase()
{
count++;
setChanged();
notifyObservers( INCREASE );
}
}
If there are several of changes at once, you may not want each change to trigger an update

It might be inefficient or cause too many screen updates

class Counter extends Observable 
{// some code removed
public void increase() { count++; }
}
{
Counter pageHits = new Counter();
pageHits.increase();
pageHits.increase();
pageHits.increase();
pageHits.notifyObservers();
}

Doc 18 Observer Slide # 18

Make sure Subject is self-consistent before Notification


Here is an example of the problem

class ComplexObservable extends Observable
     {
     Widget frontPart = new Widget();
     Gadget internalPart = new Gadget();
     
     public void trickyChange()
          {
          frontPart.widgetChange();
          internalpart.anotherChange();
          setChanged();
          notifyObservers( ); 
          }
     }
     
class MyBetterClass extends ComplexObservable
     {
     Gear backEnd = new Gear();
          
     public void trickyChange()
          {
          super.trickyChange();
          backEnd.yetAnotherChange();
          setChanged();
          notifyObservers( ); 
          }
     }


Doc 18 Observer Slide # 19
A solution to the problem

class ComplexObservable extends Observable
     {
     Widget frontPart = new Widget();
     Gadget internalPart = new Gadget();
     
     /**
      * Trigger update to observers
      */
     public void trickyChange()
          {
          doThisChangeWithFactoryMethod();
          setChanged();
          notifyObservers( ); 
          }

     private void doThisChangeWithFactoryMethod()
          {
          frontPart.widgetChange();
          internalpart.anotherChange();
          }
     }
     
class MyBetterClass extends ComplexObservable
     {
     Gear backEnd = new Gear();
          
     private void doThisChangeWithFactoryMethod()
          {
          backEnd.yetAnotherChange();
          }
     }

Doc 18 Observer Slide # 20

Adding information about the change


push model - add parameters in the update method

class IncreaseDetector extends Counter implements Observer
     { // stuff not shown
          
     public void update( Observable whatChanged, Object
message)
          {
          if ( message.equals( INCREASE) )
               increase();
          }
     }

class Counter extends Observable 
     {          // some code removed
     public void increase() 
          { 
          count++; 
          setChanged();
          notifyObservers( INCREASE ); 
          }
     }



Doc 18 Observer Slide # 21
pull model - observer asks Subject what happened

class IncreaseDetector extends Counter implements Observer
     { // stuff not shown
          
     public void update( Observable whatChanged )
          {
          if ( whatChanged.didYouIncrease() )
               increase();
          }
     }

class Counter extends Observable
{// some code removed
public void increase()
{
count++;
setChanged();
notifyObservers( );
}
}