| CS 535: Object-Oriented Programming & Design |
|
|---|
Fall Semester, 1997
Doc 23, Observer-Observable
To Lecture Notes Index
San Diego State University -- This page last updated 17-Nov-97
Contents of Doc 23, Observer-Observable
- References
- Coupling
- Separating the GUI from the Program
- Observable and Observer
- How Observable Works
- Simple Example
- Some Observer Fun - Implementing a Spreadsheet
Java API
Design Patterns: Elements of Reusable Object-Oriented Software, Gamma,
Helm, Johnson, Vlissides, pages 293-304
Measure of the interdependence among modules
"Unnecessary object coupling needlessly decreases the reusability of the
coupled objects"
"Unnecessary object coupling also increases the chances of system corruption
when changes are made to one or more of the coupled objects"
Design Goal
The interaction or other interrelationship between any two components within
the system be as weak as possible
Linked List and Output
class LinkedListCoupledToStandardOut
{
public void print()
{
System.out.println( "Here is the list" );
// add code here to print out list to standard out
}
//other methods of LinkedList not shown
}
A Better Coupling
class LinkedListCoupledToStream
{
public void print(Writer out)
{
out.write( "Here is the list" );
// add code here to print out list to out
}
//other methods of LinkedList not shown
}
class PrintingListsToTwoTypesOfOutput
{
public static void main( String args[] ) throws IOException
{
LinkedListCoupledToStream list =
new LinkedListCoupledToStream();
//pretend I added elements to the list
OutputStreamWriter outConverter =
new OutputStreamWriter( System.out );
list.print( outConverter );
Writer out = new BufferedWriter(
new FileWriter( "ListFile"));
list.print( out );
}
Coupling LinkedList to "Primitive Types"
class LinkedListCoupledToString
{
public String toString()
{
StringBuffer listBuffer = new StringBuffer();
// add code here to append list elements to the buffer
return listBuffer.toString();
}
//other methods of LinkedList not shown
}
class Test
{
public static void main( String args[] ) throws IOException
{
LinkedListCoupledToString list =
new LinkedListCoupledToString();
//pretend I added elements to the list
System.out.println( list.toString() );
Writer out = new BufferedWriter(
new FileWriter( "ListFile"));
out.write( list.toString());
}
Reduces the number of classes Linkedlist is coupled with
Increases flexibility of the Linkedlist class
A Simple Example
Problem: create a simple counter that:
- can increase
-
- can decrease
-
- can be reset to zero
Display the counter in a window and allow the user to increase or decrease the
counter via buttons.
A Poor Solution - The Window is the Counter!
class BadCounterExample extends Frame {
// create buttons for window
Button increase = new Button( "Increase" );
Button decrease = new Button( "Decrease" );
Button reset = new Button( "Reset" );
int count = 0;
public BadCounterExample( int width, int height ) {
// Set up a window
setTitle( "Button Example" );
resize( width, height );
setLayout( new FlowLayout() );
add( increase ); add( decrease );
add( reset ); show();
}
public boolean action( Event processNow, Object argument ) {
// Respond to user pushing a button
if ( processNow.target == increase ) count++;
else if ( processNow.target == decrease ) count--;
else if ( processNow.target == reset ) count = 0;
else return false;
repaint();
return true;
}
public void paint( Graphics display ) {
// Draw in window
display.drawString( "The count is " + count , 50, 50 );
}
public static void main( String args[] ){
new BadCounterExample( 200, 100 );
}
}
Output After clicking Increase 6 times
What is Wrong with the about program?It works!
Where is the code reuse?
Counter No Coupling
class Counter
{
private int count = 0;
public void increase()
{
count++;
}
public void decrease()
{
count--;
}
public void reset()
{
count = 0;
}
public int value()
{
return count;
}
public String toString()
{
return String.valueOf( count );
}
}
What Happens when there are Two Views on Count?
When view A changes the counter how does view B know to update?
Who's responsibility is it to notify B of the change?
(Dependents)(Publish-Subscribe)
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

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
An observable keeps track of when it has changed
To do this an observable must register changes with it self
Register changes with setChange message, which sets a changed flag to
true
When the observable thinks the observer should know about all the changes that
occurred, call the method notifyObservers
if the changed flag is true
- notifyObservers informs the observers that changes have taken place and
- sets the change bit to false
if the change bit is false
- notifyObservers does nothing
Observers are notified of a change by sending them the update message
the update message has two arguments
- the object (observable) that changed
-
- A message from the observable
class SimpleCounter extends Observable{
private int count = 0;
public void increase() {
count++;
setChanged();
notifyObservers();
}
}
class SimpleObserver implements Observer {
String id;
public SimpleObserver( String id ) {
this.id = id ;
}
public void update( Observable sender, Object message ) {
System.out.println( "From " + id + " New value " +
((SimpleCounter) sender).count );
}
}
class ObserveTest {
public static void main( String args[] ) {
SimpleCounter test = new SimpleCounter();
SimpleObserver a = new SimpleObserver( "a" );
test.addObserver( a);
test.addObserver( new SimpleObserver( "b" ) );
test.increase();
test.addObserver( new SimpleObserver( "c" ) );
test.increase();
}
}
Running the Example
class ObserveTest {
public static void main( String args[] ) {
SimpleCounter test = new SimpleCounter();
SimpleObserver a = new SimpleObserver( "a" );
test.addObserver( a);
test.addObserver( new SimpleObserver( "b" ) );
test.increase();
test.addObserver( new SimpleObserver( "c" ) );
test.increase();
}
}
Output
From b New value 1
From a New value 1
From c New value 2
From b New value 2
From a New value 2
Adding the Same Observer Twice?
class ObserveTest {
public static void main( String args[] ) {
SimpleCounter test = new SimpleCounter();
SimpleObserver a = new SimpleObserver( "a" );
test.addObserver( a);
test.addObserver( a );
test.increase();
test.addObserver( a );
test.increase();
}
}
Output
From a New value 1
From a New value 2
Deleting Observers
class ObserveTest {
public static void main( String args[] ) {
SimpleCounter test = new SimpleCounter();
SimpleObserver a = new SimpleObserver( "a" );
SimpleObserver b = new SimpleObserver( "b" );
test.addObserver( a);
test.addObserver( b);
test.increase();
System.out.println( "Delete a" );
test.deleteObserver( a );
test.increase();
System.out.println( "Delete a again" );
test.deleteObserver( a );
test.increase();
System.out.println( "Delete all" );
test.deleteObservers( );
test.increase();
}
}
Output
From b New value 1
From a New value 1
Delete a
From b New value 2
Delete a again
From b New value 3
Delete all
Sending Messages to The Observer
class SimpleCounter extends Observable{
private int count = 0;
public void increase() {
count++;
setChanged();
notifyObservers( "Hi Mom" );
}
}
class SimpleObserver implements Observer {
String id;
public SimpleObserver( String id ) {
this.id = id ;
}
public void update( Observable sender, Object message ) {
System.out.println( "From " + id + " With message: " +
message);
}
}
class ObserveTest {
public static void main( String args[] ) {
SimpleCounter test = new SimpleCounter();
SimpleObserver a = new SimpleObserver( "a" );
test.addObserver( a);
test.increase();
test.increase();
}
}
Output
From a With message: Hi Mom
From a With message: Hi Mom
What if My Class can not extend Observable?
Use Object composition!
class SimpleCounter {
private int count = 0;
private Observable myObservers = new Observable();
public void addObserver( Observer newObserver ) {
myObservers.addObserver( newObserver );
}
public void increase() {
count++;
changeNotify();
}
protected void changeNotify() {
myObservers.setChanged();
myObservers.notifyObservers( );
}
}
How to implement the following 3 cells?
Use Observer-Observable