本文已参与「新人创作礼」活动,一起开启掘金创作之路
首先来看看观察者模式的概念
在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新
( 例子参考自《HeadFirst设计模式》)
假设现在有一个气象站,多个公告板,当气象站数据发生变化时,这些公告板的显示内容也要进行相应的更新
先来开一个错误的示范:
//更新方法
public void measurementsChanged(){
//获取温度、湿度、气压值
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
//更新公告板
currentConditionsDisplay.update(temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
}
这样的代码是针对具体实现编程,而不是针对接口编程,如果我们要加新的公告板,就要修改代码,而且我们无法在运行的时候动态增加或删除公告板。所以,我们得把需要动态改变的地方封装起来
在观察者模式中,存在主题(Subject)和观察者(Observer),观察者可以注册进相应的主题,也可以从主题中将自己移出来,主题对象管理的数据会通知所有注册过的观察者,一旦观察者将自己移除,那么主题就不再通知这个观察者
在这个例子中,气象站就相当于主题,各个公告板就相当于观察者,一个主题可以被多个观察者观察
我们先来建立所需要的接口
//主题接口
public interface Subject {
//注册观察者
public void registerObserver(Observer observer);
//移除观察者
public void removeObserver(Observer observer);
//通知观察者
public void notifyObserver();
}
//观察者接口
public interface Observer {
public void update(float temp, float humidity, float pressure);
}
//公告板接口
public interface DisplayElement {
public void display(); //目前公告板只有一个显示数据的方法,后续你也可以添加更多的方法
}
接口建立完毕,我们来实现气象站
public class WeatherData implements Subject{
//主题所拥有的观察者
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
int i = observers.indexOf(observer);
if(i >= 0) {
observers.remove(i);
}
}
@Override
public void notifyObserver() {
for(int i = 0; i < observers.size(); i++) {
Observer observer = (Observer)observers.get(i);
observer.update(temperature, humidity, pressure);
}
}
public void measurementsChanged() {
notifyObserver();
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
再建立两个公告板
public class StatisticsDisplay implements Observer, DisplayElement{
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public StatisticsDisplay(Subject subject) {
this.weatherData = subject;
weatherData.registerObserver(this);
}
@Override
public void display() {
System.out.println("这是 statistics display");
System.out.println("现在的温度是:" + temperature);
System.out.println("现在的湿度是:" + humidity);
System.out.println("现在的大气压是:" + pressure);
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
}
public class CurrentConditionsDisplay implements Observer, DisplayElement{
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public CurrentConditionsDisplay(Subject subject) {
this.weatherData = subject;
weatherData.registerObserver(this);
}
@Override
public void display() {
System.out.println("这是 weather station");
System.out.println("现在的温度是:" + temperature);
System.out.println("现在的湿度是:" + humidity);
System.out.println("现在的大气压是:" + pressure);
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
}
最后启动气象站
public class WeatherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay conditionsDisplay = new CurrentConditionsDisplay(weatherData);
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
weatherData.setMeasurements(80.0f, 65.0f, 30.4f);
weatherData.setMeasurements(70.0f, 55.0f, 20.4f);
}
}
当我们需要动态增加或删除公告板时,只要调用weatherData的registerObserver()方法和removeObserver()方法就可以达到动态更新的效果了