Observer Pattern
Overview
Observer is a behavioural pattern that lets you define a subscription mechanism to notify multiple objects about any events that happen to an observed object.
Advantages
- Avoids tight coupling between subject and observers as subjects do not need to know about implementation details of the observers.
- Allows dynamic registration and deregistration.
Disadvantages
- Change in subject may result in chain of updates to observers and in turn their dependent objects - resulting in complex update behaviour.
- Careful attention must be paid to manage such dependencies.
Applicability
- When changes to the state of one object may require changing other objects, and the actual set of objects is unknown beforehand or changes dynamically.
- Highly applicable to GUI programming where changes to some object must notify such a change to its corresponding GUI object.
Push vs Pull Implementation
Push:
update(data1, data2, ...)
- Subject passes down only the necessary information to observers.
Pull:
update(this)
- Subject passes a reference of itself down to observers.
- Observers are then responsibile for grabbing required data.
Example
// A subject interface that represents the class to be observed.
// This is usually implemented by a more complex class with other behaviours.
public interface Subject {
public void subscribe(Observer observer);
public void unsubscribe(Observer observer)
public void notify();
}
// An observer that executes some behaviour when notified by the subject.
// This is often a state change in the subject.
public interface Observer {
public void update(Subject subject);
}
public class Thermometer implements Subject {
List<Observer> observers = new ArrayList<Observer>();
double temperature = 0.0;
@Override
public void subscribe(Observer observer) {
if (!observers.contains(observer))
observers.add(observer);
}
@Override
public void unsubscribe(Obvserver observer) {
observers.remove(observer);
}
@Override
public void notify() {
for (Observer observer : observers)
observer.update(this);
}
public double setTemperature(double temperature) {
this.temperature = temperature;
notify();
}
public class DisplayUSA implements Observer {
@Override
public void update(Subject subject) {
if (subject instanceof Thermometer) update((Thermometer)subject);
}
public void update(Thermometer subject) {
display(temperatureInC);
}
public void update(Hydrometer subject) { ... }
// And other update methods for other class instances of the subject...
public void display(int temperatureInC) {
System.out.println("From DisplayUSA: Temperature is " + convertToF(temperatureInC) + " C");
}
public double convertToF(double temperatureInC) {
return temperatureInC * (9.0 / 5.0) + 32;
}
}