Lecture 10 Observer Pattern
Joseph Haugh
University of New Mexico
Free Recall
- Get out a sheet of paper or open a text editor
- For 2 minutes write down whatever comes to mind about the last class
- This could be topics you learned
- Questions you had
- Connections you made
A Weather Monitoring Application
- We have a weather station with humidity, temperature, and pressure sensors
- We are implementing a Weather Data object that pulls data from the weather station.
- Create an app that uses the Weather Data object to update three different displays:
- current conditions
- weather stats
- forecast
Problem Specification
- The WeatherData class has getters and setters for temperature, humidity, and pressure
- The measurementsChanged() method is called anytime new weather data is available
- We don’t know or care how!
- We need to implement three different display elements that use the weather data
- The system must be expandable, in case others want to add other display elements later
A First Attempt At Coding
public class WeatherData {
//instance variable declarations
public void measurementsChanged() {
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
currentConditionsDisplay.update(temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
}
// other methods
}
A First Attempt At Coding
public class WeatherData {
//instance variable declarations
public void measurementsChanged() {
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
currentConditionsDisplay.update(temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
}
// other methods
}
- What’s wrong?
- Coding to implementations: adding displays requires changing the program
A First Attempt At Coding
public class WeatherData {
//instance variable declarations
public void measurementsChanged() {
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
currentConditionsDisplay.update(temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
}
// other methods
}
- What’s wrong?
- Coding to implementations: adding displays requires changing the program
- Encapsulate things that change!
Publish/Subscribe
- Newspapers and magazines
- Email lists
- RSS feeds
- Following someone on Twitter
- You subscribe and receive any new additions
- You unsubscribe and stop receiving anything
The Observer Pattern
- The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependencies are notified and updated automatically.
The Observer Pattern Vocabulary
- If you understand how newspaper subscriptions work then you basically already understand the observer pattern.
- However, instead saying publisher as we would in the newspaper example we instead say Subject and instead of saying subscribers we say Observers
The Observer Pattern
![]()
The Observer Pattern
- Objects use the Subject interface to (de)register as observers
- Each subject can have many observers
- All potential observers need to implement the Observer interface and provide the update() method
- A concrete subject always implements the Subject interface and the notifyObservers() method
- Concrete observers can be any class that implements the Observer interface and registers with a concrete subject
The Power of Loose Coupling
- The only thing a subject knows about an observer is that it implements a given interface
- We can add new observers at any time
- We never need to modify the subject to add new types of observers
- We can reuse subjects or observers independently of each other
- Changes to either the subject or an observer will not affect each other
- Loosely coupled designs allow us to build flexible OO systems that can handle change because they minimize the interdependencies between objects.
Weather Data Interfaces
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
public interface Observer {
public void update(float temp,
float humidity,
float pressure);
}
public interface DisplayElement {
public void display();
}
- Any potential problems with this?
Implementing The Subject Interface
public class WeatherData implements Subject {
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList<>();
}
public void registerObserver(Observer o) {
observers.add(o);
}
public void removeObserver(Observer o) {
observers.remove(o);
}
}
Notify Methods
public void notifyObservers() {
for(Observer observer : observers) {
observer.update(temperature, humidity, pressure);
}
}
public void measurementsChanged() {
notifyObservers();
}
A Display Element
public class CurrentConditionsDisplay
implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
display();
}
public void display() {
System.out.println("Current conditions: " + temperature
+ "F degrees and " + humidity + "% humidity");
}
}
Java’s Built-In Observer Pattern
- Java provides the Observer interface and the Observable class in the package java.util
- Similar to Subject and Observer
- Must extend Observable rather than implement Subject
Another Design…
![]()
The Java Observer Pattern
- For an object to become an observer
- Just implement the Observer interface (as before)
- For the observable to send notifications
- Become observable by extending the java.util.Observable superclass
- Call the setChanged() method to signify that the state of the object has changed
- Call one of two notification methods:
- notifyObservers()
- notifyObservers(Object arg)
The Dark Side of Java Observables
- Observable is a class, not an interface
- You have to subclass it, so you can’t add the Observable behavior onto a class that already extends something else
- Limits reuse potential
- Because there’s no Observable interface, you cannot create your own implementations of Observables
- Observable protects crucial methods
- E.g., setChanged() can only be called by subclasses
- Limits flexibility
- You cannot favor composition over inheritance
Should I Use Java Observables?
- Short Answer: NO
- Java’s Observable library was deprecated in Java 9
- This does not mean that the observer pattern is deprecated just that this particular implementation is
- What should you use instead?
- You can roll your own implementation to have more flexibility and control
- Use PropertyChangeSupport/Listener from java.beans but with many caveats
What Is A Bean?
![]()
- Standard describing a class that has the following properties:
- All fields are private and have getters/setters
- Has a public no argument constructor
- Implements Serializable
What Is Serializable?
- An interface which allows your object to be serialized/deserialized
- By serializing an object you transform it into a stream of bytes
- It can then be saved to disk or sent over the network
- Deserializing is the reverse, turning a stream of bytes into an object
- Can you see the problem?
Risks Of Serializing
- Bytes can easily be manipulated and made to harm your computer!
- It also means that nothing in your object (which is included in serialization) can ever change!
- Item 85 and 86 in Effective Java go into this topic more in depth
- Long story short avoid Serializable in most cases
What About Observable Then?
- So then what is the best way to implement this pattern?
- Let’s go into some code, you can download it here
- You will quickly see why no builtin will ever suffice because we have so many options!