CS 635 Advanced Object-Oriented Design & Programming Spring Semester, 2001 Observer |
||
---|---|---|
© 2001, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 20-Mar-01 |
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
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 ); }
Squeak Smalltalk Implementation [1]
Squeak |
Java |
Observer
Pattern
|
Object |
Observer |
Abstract
Observer class
|
Model |
Observable
|
Subject
class
|
Squeak Example
Model subclass: #Counter instanceVariableNames: 'count ' classVariableNames: '' poolDictionaries: '' category: 'Whitney-Examples'Class Methods
new ^super new initializeInstance Methods printOn: aStream aStream nextPutAll: count printString decrease count := count - 1. self changed: #Decreased increase count := count + 1. self changed: #Increased initialize count := 0
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
Squeak Example - Continued | counter | counter := Counter new. IncreaseDetector on: counter. counter increase; decrease; decrease; increase
Consequences
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
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 MyBetterClass 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 MyBetterClass extends ComplexObservable { Gear backEnd = new Gear(); private void 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
[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