Java Design Patterns (2/23): Observer Pattern

definition

The Observer pattern defines a one-to-many dependency between objects so that when an object changes state, all its dependants are notified and automatically updated.
insert image description here

OO design principles: strive for loosely coupled design between interacting objects.

Case: Weather Monitoring Application

generalize

The three parts in this system are

  1. Weather station (a physical device that obtains actual weather data)
  2. WeatherData object (tracks data from weather stations and updates billboards)
  3. Notice board (display the current weather conditions for users to see)

The WeatherData object knows how to contact the physical weather station for updated data.

The WeatherData object then updates the display of the three billboards:

  1. Current conditions (temperature, humidity, air pressure),
  2. meteorological statistics
  3. weather forecast.

The goal is to build an application that uses the WeatherData object to get data and update three bulletin boards: current conditions, weather statistics, and weather forecast.

insert image description here

design diagram

insert image description here

Grading over

Weather station main program

public class WeatherStation {
    
    

	public static void main(String[] args) {
    
    
		WeatherData weatherData = new WeatherData();
	
		CurrentConditionsDisplay currentDisplay = 
			new CurrentConditionsDisplay(weatherData);
		StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
		ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);

		weatherData.setMeasurements(80, 65, 30.4f);
		
		System.out.println("---");
		weatherData.setMeasurements(82, 70, 29.2f);
		
		System.out.println("---");
		weatherData.setMeasurements(78, 90, 29.2f);
		
		System.out.println("---Remove Observer---");
		weatherData.removeObserver(forecastDisplay);
		weatherData.setMeasurements(62, 90, 28.1f);
	}
}

Bulletin board

print presentation interface

public interface DisplayElement {
    
    
	public void display();
}

theme

public interface Subject {
    
    
	public void registerObserver(Observer o);
	public void removeObserver(Observer o);
	public void notifyObservers();
}
Meteorological data implementing the subject interface
import java.util.*;

public class WeatherData implements Subject {
    
    
	private List<Observer> observers;
	private float temperature;
	private float humidity;
	private float pressure;
	
	public WeatherData() {
    
    
		observers = new ArrayList<Observer>();
	}
	
	public void registerObserver(Observer o) {
    
    
		observers.add(o);
	}
	
	public void removeObserver(Observer o) {
    
    
		observers.remove(o);
	}
	
	public void notifyObservers() {
    
    
		for (Observer observer : observers) {
    
    
			observer.update(temperature, humidity, pressure);
		}
	}
	
	public void measurementsChanged() {
    
    
		notifyObservers();
	}
	
	public void setMeasurements(float temperature, float humidity, float pressure) {
    
    
		this.temperature = temperature;
		this.humidity = humidity;
		this.pressure = pressure;
		measurementsChanged();
	}

	public float getTemperature() {
    
    
		return temperature;
	}
	
	public float getHumidity() {
    
    
		return humidity;
	}
	
	public float getPressure() {
    
    
		return pressure;
	}

}

observer

public interface Observer {
    
    
	public void update(float temp, float humidity, float pressure);
}
A current status bulletin board that implements the observer interface
public class CurrentConditionsDisplay implements Observer, DisplayElement {
    
    
	private float temperature;
	private float humidity;
	private WeatherData weatherData;
	
	public CurrentConditionsDisplay(WeatherData weatherData) {
    
    
		this.weatherData = weatherData;
		weatherData.registerObserver(this);
	}
	
	public void update(float temperature, float humidity, float pressure) {
    
    
		this.temperature = temperature;
		this.humidity = humidity;
		display();
	}
	
	public void display() {
    
    
		System.out.println("Current conditions: " + temperature 
			+ "F degrees and " + humidity + "% humidity");
	}
}
Meteorological Statistics Bulletin Board Implementing the Observer Interface
public class StatisticsDisplay implements Observer, DisplayElement {
    
    
	private float maxTemp = 0.0f;
	private float minTemp = 200;
	private float tempSum= 0.0f;
	private int numReadings;
	private WeatherData weatherData;

	public StatisticsDisplay(WeatherData weatherData) {
    
    
		this.weatherData = weatherData;
		weatherData.registerObserver(this);
	}

	public void update(float temp, float humidity, float pressure) {
    
    
		tempSum += temp;
		numReadings++;

		if (temp > maxTemp) {
    
    
			maxTemp = temp;
		}
 
		if (temp < minTemp) {
    
    
			minTemp = temp;
		}

		display();
	}

	public void display() {
    
    
		System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings)
			+ "/" + maxTemp + "/" + minTemp);
	}
}
A weather forecast bulletin board that implements the observer interface
public class ForecastDisplay implements Observer, DisplayElement {
    
    
	private float currentPressure = 29.92f;  
	private float lastPressure;
	private WeatherData weatherData;

	public ForecastDisplay(WeatherData weatherData) {
    
    
		this.weatherData = weatherData;
		weatherData.registerObserver(this);
	}

	public void update(float temp, float humidity, float pressure) {
    
    
        lastPressure = currentPressure;
		currentPressure = pressure;

		display();
	}

	public void display() {
    
    
		System.out.print("Forecast: ");
		if (currentPressure > lastPressure) {
    
    
			System.out.println("Improving weather on the way!");
		} else if (currentPressure == lastPressure) {
    
    
			System.out.println("More of the same");
		} else if (currentPressure < lastPressure) {
    
    
			System.out.println("Watch out for cooler, rainy weather");
		}
	}
}

operation result

The running result of the main program of the weather station:

Current conditions: 80.0F degrees and 65.0% humidity
Avg/Max/Min temperature = 80.0/80.0/80.0
Forecast: Improving weather on the way!
---
Current conditions: 82.0F degrees and 70.0% humidity
Avg/Max/Min temperature = 81.0/82.0/80.0
Forecast: Watch out for cooler, rainy weather
---
Current conditions: 78.0F degrees and 90.0% humidity
Avg/Max/Min temperature = 80.0/82.0/78.0
Forecast: More of the same
---Remove Observer---
Current conditions: 62.0F degrees and 90.0% humidity
Avg/Max/Min temperature = 75.5/82.0/62.0

off-the-shelf wheels

The Java API has a built-in observer pattern. The java.util package contains the most basic Observer interface and Observable class, which is very similar to the Subject interface and Observer interface of the case.

The Observer interface and the Observable class are more convenient to use because many functions are already prepared in advance.

package java.util;

@Deprecated(since="9")
public interface Observer {
    
    

    void update(Observable o, Object arg);
}
package java.util;

@Deprecated(since="9")
public class Observable {
    
    
    private boolean changed = false;
    private Vector<Observer> obs;

    public Observable() {
    
    
        obs = new Vector<>();
    }

    public synchronized void addObserver(Observer o) {
    
    
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
    
    
            obs.addElement(o);
        }
    }

    public synchronized void deleteObserver(Observer o) {
    
    
        obs.removeElement(o);
    }

    public void notifyObservers() {
    
    
        notifyObservers(null);
    }

    public void notifyObservers(Object arg) {
    
    
        /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Object[] arrLocal;

        synchronized (this) {
    
    
            /* We don't want the Observer doing callbacks into
             * arbitrary code while holding its own Monitor.
             * The code where we extract each Observable from
             * the Vector and store the state of the Observer
             * needs synchronization, but notifying observers
             * does not (should not).  The worst result of any
             * potential race-condition here is that:
             * 1) a newly-added Observer will miss a
             *   notification in progress
             * 2) a recently unregistered Observer will be
             *   wrongly notified when it doesn't care
             */
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    public synchronized void deleteObservers() {
    
    
        obs.removeAllElements();
    }

    protected synchronized void setChanged() {
    
    
        changed = true;
    }

    protected synchronized void clearChanged() {
    
    
        changed = false;
    }

    public synchronized boolean hasChanged() {
    
    
        return changed;
    }

    public synchronized int countObservers() {
    
    
        return obs.size();
    }
}

Note : Observer and Observable are deprecated in Java 9.

Deprecated. This class and the Observer interface have been deprecated. The event model supported by Observer and Observable is quite limited, the order of notifications delivered by Observable is unspecified, and state changes are not in one-for-one correspondence with notifications. For a richer event model, consider using the java.beans package. For reliable and ordered messaging among threads, consider using one of the concurrent data structures in the java.util.concurrent package. For reactive streams style programming, see the java.util.concurrent.Flow API.

Link

References

  1. "Head First Design Patterns"
  2. Observable (Java SE 9 & JDK 9 )
  3. Java 9: ​​Reasons and solutions for the obsolescence of Observer and Observable

Guess you like

Origin blog.csdn.net/u011863024/article/details/119766071