CS 635 Advanced Object-Oriented Design & Programming Spring Semester, 2002 Observer |
||
---|---|---|
© 2002, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 11-Apr-02 |
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:
Structure
Collaborations
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. } }
Consequences
Smalltalk Implementation
Smalltalk |
Java |
Observer
Pattern
|
Object |
Observer |
Abstract
Observer class
|
Object
& Model
|
Observable
|
Subject
class
|
Object methods
update: anAspectSymbol
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
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]
Smalltalk Example - Continued | counter | counter := Counter new. IncreaseDetector on: counter. counter increase; decrease; decrease; increase
Java’s Implementation
Java API implements a framework for this pattern
Java |
Observer
Pattern
|
Interface
Observer
|
Abstract
Observer class
|
Observable
class
|
Subject
class
|
java.util.Observable Methods
addObserver(Observer)
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 ); } }
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()); } } }
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(); } }
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(); } }
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 ); }
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); } }
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]
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
Who Triggers the update?
class Counter extends Observable { // some code removed public void increase() { count++; setChanged(); notifyObservers( INCREASE ); } }
class Counter extends Observable { // some code removed public void increase() { count++; } } Counter pageHits = new Counter(); pageHits.increase(); pageHits.increase(); pageHits.increase(); pageHits.notifyObservers();
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( ); } }
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(); } }
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 ); } }
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( ); } }
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
Java 1.1 Event Model
Each component supports different types of events:
Component supports
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
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
Copyright ©, All rights reserved.
2002 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
OpenContent license defines the copyright on this document.
Previous    visitors since 11-Apr-02    Next