Observer Pattern Code in Java
Implementation Issues
- Question: How does the subject keep track of its observers?
- What if an observer wants to observe more than one subject?
Have the subject tell the observer who it is via the update interface
- Question: Who triggers the update?
The subject whenever its state changes
The observers after they cause one or more state changes
Some third party object(s)
- Make sure the subject updates its state before sending out notifications
- Question: How much info about the change should the subject send to the observers?
a) Push Model - Lots, b) Pull Model - Very Little
- Can the observers subscribe to specific events of interest?
- Can an observer also be a subject?
- What if an observer wants to be notified only after several subjects have changed state?
- Use an intermediary object which acts as a mediator
- Subjects send notifications to the mediator object which performs any necessary processing before notifying the observers
Known Uses
- Smalltalk Model/View/Controller user interface framework
Model = Subject, View = Observer, Controller is whatever object changes the state of the subject
- Java 1.1 AWT/Swing Event Model
Related Patterns
Mediator: To encapsulate complex update semantics
Java Implementation of the Observer
We could implement the Observer pattern “from scratch” in Java
- But Java provides the Observable/Observer classes as built-in support for the Observer pattern
- The java.util.Observable class is the base Subject class. Any class that wants to be observed extends this class.
- Provides methods to add/delete observers
- Provides methods to notify all observers
- A subclass only needs to ensure that its observers are notified in the appropriate mutators
- Uses a Vector for storing the observer references
- The java.util.Observer interface is the Observer interface. It must be implemented by any observer class.
java.util.Observable Class Methods
- public Observable() - Construct an Observable with zero Observers
- public synchronized void addObserver(Observer o) - Adds an observer to the set of observers of this object
- public synchronized void deleteObserver(Observer o) - Deletes an observer from the set of observers of this object
- protected synchronized void setChanged() - Indicates that this object has changed
- protected synchronized void clearChanged() - Indicates that this object has no longer changed, or that it has already
notified all of its observers of its most recent change. This method is called automatically by notifyObservers().
- public synchronized boolean hasChanged() - Tests if this object has changed. Returns true if setChanged() has been
called more recently than clearChanged() on this object; false otherwise.
- public void notifyObservers(Object arg) - If this object has changed, as indicated by the hasChanged() method, then
notify all of its observers and then call the clearChanged() method to indicate that this object has no longer changed. Each observer has its
update() method called with two arguments: this observable object and the arg argument. The arg argument can be used to indicate which attribute of
the observable object has changed.
- public void notifyObservers() - Same as above, but the arg argument is set to null. That is, the observer is
given no indication what attribute of the observable object has changed.
- public abstract void update(Observable o, Object arg) - This method is called whenever the observed object is changed. An
application calls an observable object's notifyObservers method to have all the object's observers notified of the change.
Parameters: 1) o - the observable object, 2) arg - an argument passed to the notifyObservers method
/** A subject to observe! */
public class ConcreteSubject extends Observable {
private String name;
private float price;
public ConcreteSubject(String name, float price) {
this.name = name;
this.price = price;
System.out.println("ConcreteSubject created: " + name + " at " + price);
}
public String getName() {return name;}
public float getPrice() {return price;}
public void setName(String name) {
this.name = name;
setChanged();
notifyObservers(name);
}
public void setPrice(float price) {
this.price = price;
setChanged();
notifyObservers(new Float(price));
}
}
// An observer of name changes.
public class NameObserver implements Observer {
private String name;
public NameObserver() {
name = null;
System.out.println("NameObserver created: Name is " + name);
}
public void update(Observable obj, Object arg) {
if (arg instanceof String) {
name = (String)arg;
System.out.println("NameObserver: Name changed to " + name);
}
else {
System.out.println("NameObserver: Some other change to subject!");
}
}
}
// An observer of price changes.
public class PriceObserver implements Observer {
private float price;
public PriceObserver() {
price = 0;
System.out.println("PriceObserver created: Price is " + price);
}
public void update(Observable obj, Object arg) {
if (arg instanceof Float) {
price = ((Float)arg).floatValue();
System.out.println("PriceObserver: Price changed to" + price);
}
else {
System.out.println(”PriceObserver: Some other change to subject!");
}
}
// Test program for ConcreteSubject, NameObserver and PriceObserver
public class TestObservers {
public static void main(String args[]) {
// Create the Subject and Observers.
ConcreteSubject s = new ConcreteSubject("Corn Pops", 1.29f);
NameObserver nameObs = new NameObserver();
PriceObserver priceObs = new PriceObserver();
// Add those Observers!
s.addObserver(nameObs);
s.addObserver(priceObs);
// Make changes to the Subject.
s.setName("Frosted Flakes");
s.setPrice(4.57f);
s.setPrice(9.22f);
s.setName("Sugar Crispies");
}
}