SDSU CS 635 Advanced Object-Oriented Design & Programming
Spring Semester, 2001
Observer
Previous    Lecture Notes Index    Next    
© 2001, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 20-Mar-01

Contents of Doc 10, Observer



References

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

The Design Patterns Smalltalk Companion, Alpert, Brown, Woolf, Addision-Wesley, 1998

Java API

Squeak API

VisualWorks Smalltalk API

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

Structure





Doc 10, Observer Slide # 4

Collaborations






Doc 10, Observer Slide # 5

Java’s Implementation


Java API implements a framework for this pattern

Java
Observer Pattern
Interface Observer
Abstract Observer class
Observable class
Subject class


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

A Java Example


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 10, Observer Slide # 8
class IncreaseDetector implements Observer
   {
   public void update( java.util.Observable whatChanged, 
                     java.lang.Object message)
      {
      if ( message.equals( Counter.INCREASE) ) 
         {
         Counter increased = (Counter) whatChanged;
         System.out.println( increased.label() + " changed to " + 
                              increased.value());
         }   
      }
   }


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

class IncreaseButton extends CounterButton
   {
   public IncreaseButton( Counter count )
      { 
      super( "Increase", count ); 
      }
   
   protected void changeCounter()
      { 
      count.increase(); 
      }   
   }


Doc 10, Observer Slide # 10

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

class ButtonController extends Frame
   {
   public ButtonController( Counter model, int x, int y, 
                        int width, int height )
      {
      setTitle( model.label() );
      reshape(x, y,  width, height );
      setLayout( new FlowLayout() );
      
      // buttons to change counter
      add( new IncreaseButton( model ));
      add( new DecreaseButton( model ));
      show();
      }
   }


Doc 10, Observer Slide # 11
Sample Program

class TestButton
   {
   public  static  void  main( String  args[] ){
      Counter x = new Counter( "x" );
      Counter y = new Counter( "y" );
   
      IncreaseDetector plus = new IncreaseDetector(  );
      x.addObserver( plus ); 
      y.addObserver( plus ); 
   
      new ButtonController( x,  30, 30, 150, 50 );
      new ButtonController( y,  30, 100, 150, 50 );
      }


Doc 10, Observer Slide # 12

Squeak Smalltalk Implementation [1]


Squeak
Java
Observer Pattern
Object
Observer
Abstract Observer class
Model
Observable
Subject class

Object methods

update: aParameter
Receive an update message from a Model(Subject)
changed
Receiver changed in a general way; inform all the dependents by sending each dependent an update: message.

changed: aParameter
Receiver changed. The change is denoted by the argument aParameter. Inform all of the dependents.

addDependent: anObject

removeDependent: anObject

dependents
return collection of all dependents

Object implements methods for both Observer and Subject.

Actual Subjects should subclass Model

Model handles dependents better


Doc 10, Observer Slide # 13

Squeak Example


Model subclass: #Counter
   instanceVariableNames: 'count '
   classVariableNames: ''
   poolDictionaries: ''
   category: 'Whitney-Examples'
   
Class Methods

new
   ^super new initialize
Instance Methods
printOn: aStream
   aStream nextPutAll: count printString
   
decrease
   count := count - 1.
   self changed: #Decreased
   
increase
   count := count + 1.
   self changed: #Increased
   
initialize
   count := 0


Doc 10, Observer Slide # 14
Squeak Example - Continued

Object subclass: #IncreaseDetector
   instanceVariableNames: 'model '
   classVariableNames: ''
   poolDictionaries: ''
   category: 'Whitney-Examples'

Class Methods

on: aCounter
   ^super new setModel: aCounter

Instance Methods

update: aSymbol
   aSymbol = #Increased
      ifTrue:
         [Transcript 
            show: 'Count is now: ' , model printString;
            cr]
   
setModel: aCounter
   model := aCounter.
   aCounter addDependent: self




Doc 10, Observer Slide # 15
Squeak Example - Continued

| counter |
counter := Counter new.
IncreaseDetector on: counter.
counter
   increase;
   decrease;
   decrease;
   increase


Doc 10, Observer Slide # 16

Consequences





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



Subject can not perform any work until all observers are done


Doc 10, Observer Slide # 17

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 10, Observer Slide # 18
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 10, Observer Slide # 19
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 10, Observer Slide # 20
A Template Method Solution to the Problem

class ComplexObservable extends Observable
   {
   Widget frontPart = new Widget();
   Gadget internalPart = new Gadget();
   
   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 10, Observer Slide # 21
Adding information about the change

push models - 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 10, Observer Slide # 22
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( ); 
         }
      }


Doc 10, Observer Slide # 23

Scaling the Pattern


Java uses the Observer pattern in AWT and Swing

AWT & Swing components broadcast events(change) to Observers(Listeners)

In Java 1.0 AWT components broadcast each event to all its listeners

Usually each listener was interested in a particular type of event

As the number of AWT components & listeners grew programs slowed down


Doc 10, Observer Slide # 24
Java 1.1 Event Model

Each component supports different types of events:

Component supports
ComponentEvent
FocusEvent
KeyEvent
MouseEvent

Each event type supports one or more listener types:

MouseEvent supports
MouseListener
MouseMotionListener

Each listener interface replaces update with multiple methods

MouseListener interface has:
mouseClicked()
mouseEntered()
mousePressed()
mouseReleased()

A mouse listener (observer) has to implement all 4 methods

Listeners


Doc 10, Observer Slide # 25
Small Models

Often an object has a number of fields(aspects) of interest to observers

Rather than make the object a subject make the individual fields subjects



Squeak ValueHolder

Subject for one value

ValueHolder allows you to:


Setting the value notifies the observers of the change


Doc 10, Observer Slide # 26
Adapting Observers

An observer implements an update method

A concrete observer represents an abstraction

Update() may be out of place in this abstraction

Use an adapter to map update() method to a different method in the concrete observer


VisualWorks Smalltalk has a built-in adapter DependencyTransformer


Doc 10, Observer Slide # 27
[1] VisualWorks Smalltalk has a richer implementation of Observer methods, which would simplify the Squeak example

Copyright ©, All rights reserved.
2001 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
OpenContent license defines the copyright on this document.

Previous    visitors since 20-Mar-01    Next