SDSU CS 635: Advanced Object-Oriented Design & Programming
Spring Semester, 1998
Observer

To Lecture Notes Index
© 1998, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 21-Apr-98

Contents of Doc 7, Observer


References slide # 1
Observer slide # 2
...Structure slide # 3
...Collaborations slide # 4
...SpreadSheet Example slide # 5
...Java's Implementation slide # 6
...A Java Example slide # 8
......The Classes slide # 8
......Sample Program slide # 20
...Consequences slide # 22
...Implementation Issues slide # 23



Doc 7, Observer Slide # 1

References


Design Patterns: Elements of Reusable Object-Oriented Software, Gamma, Helm, Johnson, Vlissides, 1995

Java API


Doc 7, Observer Slide # 2

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:


Doc 7, Observer Slide # 3

Structure





Doc 7, Observer Slide # 4

Collaborations







Doc 7, Observer Slide # 5

SpreadSheet Example


How to implement a spreadsheet?






Doc 7, Observer Slide # 6

Java's Implementation


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

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 done by calling the update() method on all observers.

Interface java.util.Observer

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


Doc 7, Observer Slide # 7
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.
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

Doc 7, Observer Slide # 8

A Java Example

The Classes










indicates interface

Doc 7, Observer Slide # 9
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 7, Observer Slide # 10
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 7, Observer Slide # 11
class Counter
/**
 * A counter can increase/decrease by 1. 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 7, Observer Slide # 12
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 7, Observer Slide # 13
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 7, Observer Slide # 14
class IncreaseButton
/**
 * A button that increases a counter each time it 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 it is pressed
 */

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

Doc 7, Observer Slide # 15
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 7, Observer Slide # 16
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 7, Observer Slide # 17
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 7, Observer Slide # 18
class RectangleView
/**
 * Draws a colored rectangle that depends on two counters. 
 * One counter is the width, 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 7, Observer Slide # 19
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 7, Observer Slide # 20

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 7, Observer Slide # 21
Runtime Object Interaction






Doc 7, Observer Slide # 22

Consequences


Abstract coupling between Subject and Observer

Support for broadcast communication

Unexpected updates


Simple change in subject can cause numerous updates, which can be expensive or distracting

Updates can take too long

Subject can not perform any work until all observers are done
Doc 7, Observer Slide # 23

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 7, Observer Slide # 24
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

€ Have clients call Notify at the right time

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

Doc 7, Observer Slide # 25
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 7, Observer Slide # 26
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 doThisChangeWithTemplateMethod()
          {
          frontPart.widgetChange();
          internalpart.anotherChange();
          }
     }
     
class MyBetterClass extends ComplexObservable
     {
     Gear backEnd = new Gear();
          
     private void doThisChangeWithTemplateMethod()
          {
          backEnd.yetAnotherChange();
          }
     }

Doc 7, Observer Slide # 27
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 7, Observer Slide # 28
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( );
}
}

visitors since 19-Feb-98