观察者模式的理解和使用
一、是什么?作用
1. 观察者模式定义了一系列对象之间的一对多关系
2. 当一个对象改变状态, 其他依赖着都会受到通知
二、示例
需求: 气象观测天气,并定时更新观测数据,我们的电子仪器需要接受这些数据
1. PUSH模式, 由被观察者推送给观察者
1. 定义主题, 即被观察者
/** * 主题 */ public interface Subject { // 添加观察者 void registerObserver(Observer o); // 删除观察者 void removeObserver(Observer o); // 通知 void notifyObservers(); }
2. 定义观察者的超类,里面只有一个更新的方法
/** * 观察者超类 */ public interface Observer { public void update(float temp, float humidity, float pressure); }
3. 天气数据, 主题的子类
1. 有一个属性来存放观察者
2. 实现主题的方法
3. 有三个数据需要传递(温度、湿度、气压)
1 /** 2 * 天气数据, 实现主题 3 */ 4 public class WeatherData implements Subject { 5 6 /** 7 * 存储观察者 8 */ 9 private List<Observer> observers; 10 private float temperature; 11 private float humidity; 12 private float pressure; 13 14 public WeatherData() { 15 this.observers = new ArrayList<>(); 16 } 17 18 /** 19 * 当气象站得到更新观测值时,我们通知观察者 20 */ 21 public void measurementsChanged() { 22 notifyObservers(); 23 } 24 25 /** 26 * 模拟气象站更改了气象数据 27 */ 28 public void setMeasurements(float temperature, float humidity, float pressure) { 29 // 更改气象数据 30 this.temperature = temperature; 31 this.humidity = humidity; 32 this.pressure = pressure; 33 34 // 通知观察者 35 measurementsChanged(); 36 } 37 38 @Override 39 public void registerObserver(Observer o) { 40 observers.add(o); 41 } 42 43 @Override 44 public void removeObserver(Observer o) { 45 int i = observers.indexOf(0); 46 if (i >= 0) { 47 observers.remove(0); 48 } 49 } 50 51 @Override 52 public void notifyObservers() { 53 for (Observer observer : observers) { 54 observer.update(temperature, humidity, pressure); 55 } 56 } 57 58 public float getTemperature() { 59 return temperature; 60 } 61 62 public float getHumidity() { 63 return humidity; 64 } 65 66 public float getPressure() { 67 return pressure; 68 } 69 }
4. 观察者具体类, 实现观察者,将主题通过构造传入,并订阅(这里将耦合度降到了很低了),实现更新方法
/** * 显示当前天气的仪器 - 观察者的具体类 */ public class NowConditionDisplay implements Observer { private float temperature; private float humidity; private WeatherData weatherData; public NowConditionDisplay(WeatherData weatherData) { this.weatherData = weatherData; weatherData.registerObserver(this); } @Override public void update(float temp, float humidity, float pressure) { this.temperature = temp; this.humidity = humidity; display(); } public void display() { System.out.println("[push] 当前温度:" + temperature + " 当前湿度: " + humidity); } }
测试
public class pushMain { public static void main(String[] args) { WeatherData weatherData = new WeatherData(); NowConditionDisplay nowConditionDisplay = new NowConditionDisplay(weatherData); weatherData.setMeasurements(20, 10, 29.2f); } } 控制台显示: [push] 当前温度:20.0 当前湿度: 10.0
2. PULL模式, 由观察者去拉数据
1. 天气数据提供者,利用java.util包下的Observable类来实现
import java.util.Observable; /** * 数据提供者 */ public class WeatherData extends Observable { // 三个数据 private float temperature; private float humidity; private float pressure; public WeatherData() { } public void measurementChanged() { setChanged(); notifyObservers(); } public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementChanged(); } public float getTemperature() { return temperature; } public float getHumidity() { return humidity; } public float getPressure() { return pressure; } }
2. 观察者子类, 实现java.util的Observer
import java.util.Observable; import java.util.Observer; /** * @author wb-xp570304 on 2019/7/9 */ public class NowConditionDisplay implements Observer { Observable observable; private float temperature; private float humidity; public NowConditionDisplay(Observable observable) { this.observable = observable; observable.addObserver(this); } @Override public void update(Observable o, Object arg) { if (o instanceof WeatherData) { WeatherData weatherData = (WeatherData) o; this.temperature = weatherData.getTemperature(); this.humidity = weatherData.getHumidity(); display(); } } public void display() { System.out.println("当前温度: " + temperature + " 湿度: " + humidity); } }
测试:
public class pullMain { public static void main(String[] args) { WeatherData weatherData = new WeatherData(); NowConditionDisplay nowConditionDisplay = new NowConditionDisplay(weatherData); weatherData.setMeasurements(80, 65, 30f); } }
控制台显示:
当前温度: 80.0 湿度: 65.0
小结:
第二种pull的方式这里是利用java.util.Observable来实现的,而且用的是继承,减少了代码的可扩展性
三、总结
1. 观察者模式的理念是,为交互对象之间的松耦合设计而努力