SDSU CS 635 Advanced Object-Oriented Design & Programming
Spring Semester, 2004
Observer & Singleton
Previous    Lecture Notes Index    Next    
© 2004, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 26-Feb-04


References

Design Patterns: Elements of Reusable Object-Oriented Software, Gamma, Helm, Johnson, Vlissides, 1995, pp. 293-303, 127-134

The Design Patterns Smalltalk Companion, Alpert, Brown, Woolf, Addision-Wesley, 1998, pp. 305-326, 91-101

Java API

VisualWorks Smalltalk API


Doc 11, Observer & Singleton 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 11, Observer & Singleton Slide # 3

Structure





Doc 11, Observer & Singleton Slide # 4

Collaborations






Doc 11, Observer & Singleton Slide # 5
Simple ExampleReplace
Note example does not use legal Java

public class Subject {
   Window display;
   public void someMethod() {
      this.modifyMyStateSomeHow();
      display.addText( this.text() );
   }
}
With
public class Subject {
   ArrayList observers = new ArrayList();
   
   public void someMethod() {
      this.modifyMyStateSomeHow();
      changed();
   }
   
   private void changed() {
      Iterator needsUpdate = observers.iterator();
      while (needsUpdate.hasNext() )
         needsUpdate.next().update( this );
   }
}
   
public class SampleWindow {
   public void update(Object subject) {
      text = ((Subject) subject).getText();
      etc.
   }
}

Doc 11, Observer & Singleton Slide # 6

Consequences











Doc 11, Observer & Singleton Slide # 7

Smalltalk Implementation


Smalltalk
Java
Observer Pattern
Object
Observer
Abstract Observer class
Object & Model
Observable
Subject class

Object implements methods for both Observer and Subject.

Actual Subjects should subclass Model

Model handles dependents better



Doc 11, Observer & Singleton Slide # 8
Object methods

update: anAspectSymbol
update: anAspectSymbol with: aParameter
update: anAspectSymbol with: aParameter from: aSender
Receive an update message from a Model(Subject)
changed
changed: anAspectSymbol
changed: anAspectSymbol with: aParameter
Receiver changed.


addDependent: anObject

removeDependent: anObject

dependents
return collection of all dependents


Doc 11, Observer & Singleton Slide # 9

Smalltalk Example


Smalltalk.CS635 defineClass: #Counter
   superclass: #{Core.Object}
   indexedType: #none
   private: false
   instanceVariableNames: 'count '
   classInstanceVariableNames: ''
   imports: ''
   category: 'Observer Examples'

CS635.Counter class methods
new
   ^super new initialize 

CS635.Counter instance methods
decrease
   count := count - 1.
   self changed: #decrease

increase
   count := count + 1.
   self changed: #increase

initialize
   count := 0

printOn: aStream
   aStream
      nextPutAll: count printString 


Doc 11, Observer & Singleton Slide # 10
Count Observer

Smalltalk.CS635 defineClass: #IncreaseDectector
superclass: #{Core.Object}
indexedType: #none
private: false
instanceVariableNames: 'model '
classInstanceVariableNames: ''
imports: ''
category: 'Observer Examples'

CS635.IncreaseDectector class methods

on: aCounter
   | detector |
   detector := super new.
   aCounter    addDependent: detector.
   ^detector 


CS635.IncreaseDectector instance methods

update: anAspectSymbol with: aParameter from: aSender
   anAspectSymbol = #increase ifTrue:
      [Transcript 
          show: 'Count is now: ' , aSender printString;
         cr] 


Doc 11, Observer & Singleton Slide # 11
Smalltalk Example - Continued

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



Doc 11, Observer & Singleton Slide # 12

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 11, Observer & Singleton Slide # 13
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 11, Observer & Singleton Slide # 14

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 11, Observer & Singleton Slide # 15
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 11, Observer & Singleton Slide # 16
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 11, Observer & Singleton Slide # 17
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 11, Observer & Singleton Slide # 18
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 11, Observer & Singleton Slide # 19

Implementation Issues


Mapping subjects(Observables) to observers

Use list in subject
Use hash table

public class Observable {
    private boolean changed = false;
    private Vector obs;
   
public Observable() {
        obs = new Vector();
    }
   
public synchronized void addObserver(Observer o) {
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }


Doc 11, Observer & Singleton Slide # 20
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

update: anAspectSymbol with: aParameter from: aSender
   anAspectSymbol = #increase ifTrue:
      [Transcript 
          show: 'Count is now: ' , aSender printString;
         cr] 



Doc 11, Observer & Singleton Slide # 21
Dangling references to Deleted Subjects

In C++ the subject may no longer exist

In Java/Smalltalk the subject will exists as long as reference exists

Observer reference to Subject may keep Subject around after Subject is not needed


Doc 11, Observer & Singleton Slide # 22
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 11, Observer & Singleton Slide # 23
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 MySubclass extends ComplexObservable
   {
   Gear backEnd = new Gear();
      
   public void trickyChange()
      {
      super.trickyChange();
      backEnd.yetAnotherChange();
      setChanged();
      notifyObservers( ); 
      }
   }


Doc 11, Observer & Singleton Slide # 24
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 MySubclass extends ComplexObservable
   {
   Gear backEnd = new Gear();
   private void doThisChangeWithTemplateMethod()
      {
      super. DoThisChangeWithTemplateMethod();
      backEnd.yetAnotherChange();
      }
   }


Doc 11, Observer & Singleton Slide # 25
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 11, Observer & Singleton Slide # 26
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 11, Observer & Singleton Slide # 27

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 11, Observer & Singleton Slide # 28
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 11, Observer & Singleton Slide # 29
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



VisualWorks ValueHolder

Subject for one value

ValueHolder allows you to:


Setting the value notifies the observers of the change


Doc 11, Observer & Singleton Slide # 30
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 11, Observer & Singleton Slide # 31

Singleton

Intent


Insure a class only has one instance, and provide a global point of access to it


Motivation


There are times when a class can only have one instance


Applicability


Use the Singleton pattern when





Doc 11, Observer & Singleton Slide # 32
Examples of Using a Singleton

Java Security manager
All parts of a program must access the same security manager

Once set a security manager cannot be changed in a program


Logging the activity of a server
All parts of the server should use the same instance of the logging system

The server should not be able to change the instance of the logging system was it has been set

Null Object

If Null object does not have state, only need one instance



Doc 11, Observer & Singleton Slide # 33

Implementation

Java


// Only one object of this class can be created
class Singleton
   {
   private static Singleton _instance = null;
   
   private Singleton()   { fill in the blank }
   
   public static Singleton getInstance()
      {
      if (  _instance == null )
          _instance = new Singleton();
      return _instance;
      }
   public void otherOperations() { blank; }
   }
   
class Program
   {
   public void aMethod()
      {
      X = Singleton.getInstance();
      }
   }


Doc 11, Observer & Singleton Slide # 34
Java Singletons, Classes, Garbage Collection

Classes can be garbage collected in Java

Only happens when there are

If a singleton's state is modified and its class is garbage collected, its modified state is lost


To avoid having singletons garbage collected:



Store singleton or class in system property


Doc 11, Observer & Singleton Slide # 35
Implementation

C++


// Only one object of this class can be created
class Singleton
   {
   private:
      static Singleton* _instance;
      void otherOperations();
   protected:
      Singleton();
   public:
      static Singleton* getInstance();
   }
   
   
Singleton*  Singleton::_instance = 0;
Singleton* Singleton::getInstance()
   {
   if (  _instance == 0 )
       _instance = new Singleton;
   return _instance;
   }



Doc 11, Observer & Singleton Slide # 36
Implementation

Smalltalk


Smalltalk.CS635 defineClass: #SingletonExample
   superclass: #{Core.Object}
   indexedType: #none
   private: false
   instanceVariableNames: ''
   classInstanceVariableNames: 'uniqueInstance '
   imports: ''
   category: 'Lecture notes'!


CS635.SingletonExample class methodsFor: 'instance creation'

current
   uniqueInstance isNil ifTrue:[uniqueInstance := super new].
   ^uniqueInstance

new
   self error: 'Use current to get an instance of Class: ' , self name


One could also use a private constant shared variable to store the unique instance

Doc 11, Observer & Singleton Slide # 37
Overriding new in Smalltalk

Since can control what new returns one might be tempted to use:

new
   uniqueInstance isNil ifTrue:[uniqueInstance := super new].
   ^uniqueInstance

This can be misleading; user might think they are getting different objects when calling new

Do we have two different windows below or not?

| left right |
left := SingleWindow new.
Right := SingleWindow new.
left position: 100@ 100.
right position: 500@100.


Doc 11, Observer & Singleton Slide # 38
Naming the Access Method

GOF uses: instance()

POSA 1 uses: getInstance()

Smalltalk uses default and current


Selecting names is one of the more difficult problems in object-oriented analysis. No name is perfect [1]


Doc 11, Observer & Singleton Slide # 39

Singletons and Static


If one needs only one instance of a class why not just implement all methods as static?





Doc 11, Observer & Singleton Slide # 40

Consequences




Doc 11, Observer & Singleton Slide # 41

Questions for Thought


A number of patterns seem to violate basic design principles. For example the Singleton does provide for global access. Most programmers at least will state that one should not use globals. Yet the Singleton allows one to create and use globals in a program.

1. Go through the design patterns and determine which patterns violate which basic design principles.

2. How does one justify the patterns violating the basic design principles?

Doc 11, Observer & Singleton Slide # 42
[1] Fowler pp. 9, Alpert pp. 98

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

Previous    visitors since 26-Feb-04    Next