CS 635 Advanced Object-Oriented Design & Programming Spring Semester, 2004 Observer & Singleton |
||
---|---|---|
© 2004, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 26-Feb-04 |
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
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
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
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(); } }
Java Singletons, Classes, Garbage Collection
Classes can be garbage collected in Java
Only happens when there are
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; }
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
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.
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]
Singletons and Static
If one needs only one instance of a class why not just implement all methods as static?
Consequences
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?
[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