观察者模式
观察者模式是什么?
观察者模式定义了对象之间一对多的依赖,当对象改变状态时,它的所有依赖者都会收到通知并自动更新。
观察者模式解决什么问题?
天气数据通常来源于卫星或者气象局传感器(通过setWeatherData存储),每当新的天气数据到来时,就需要提醒各个Tv频道进行更新(在WeatherData中调用dataChanged()通知各个Tv)
public class WeatherData {
private int lowTemperature;
private String weather;
private int highTemperature;
public void setWeatherData(int lowTemperature, String weather, int highTemperature) {
this.lowTemperature = lowTemperature;
this.weather = weather;
this.highTemperature = highTemperature;
dataChanged();
}
public void dataChanged() {
BeijingTv.update(lowTemperature, weather, highTemperature);
GuangdongTv.update(lowTemperature, weather, highTemperature);
}
}
class BeijingTv {
public static void update(int lowTemperature, String weather, int highTemperature) {
System.out.println("北京天气:" + weather + ",最低气温:" + lowTemperature + ",最高气温:" + highTemperature);
}
}
class GuangdongTv {
public static void update(int lowTemperature, String weather, int highTemperature) {
System.out.println("广东天气:" + weather + ",最低气温:" + lowTemperature + ",最高气温:" + highTemperature);
}
}
- problem1:当有新频道需要获取天气数据时,需要修改WeatherData的源代码
- problem2:更新的方法都是类似的
观察者模式实现1——推送数据
被观察者接口
被观察者有注册、取消注册、通知三个方法
interface Subject{
void registerObserver(Observer o);
void unregisterObserver(Observer o);
void notifyObserver();
}
观察者接口
观察者有一个通知方法
interface Observer{
void update(int lowTemperature, String weather, int highTemperature);
}
被观察者实例
WeatherData实现Subject接口,内部通过ArrayList维护观察者
public class WeatherData implements Subject {
private int lowTemperature;
private String weather;
private int highTemperature;
private ArrayList<Observer> observers;
public WeatherData() {
observers = new ArrayList<>();
}
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void unregisterObserver(Observer o) {
int i = observers.indexOf(o);
if (i > 0) {
observers.remove(o);
}
}
@Override
public void notifyObserver() {
for (int i = 0; i < observers.size(); i++) {
Observer observer = observers.get(i);
observer.update(lowTemperature, weather, highTemperature);
}
}
public void dataChanged() {
notifyObserver();
}
public void setWeatherData(int lowTemperature, String weather, int highTemperature) {
this.lowTemperature = lowTemperature;
this.weather = weather;
this.highTemperature = highTemperature;
dataChanged();
}
}
被观察者实例
各个Tv频道通过实现Observer重写update()方法完成对数据的操作
class BeijingTv implements Observer {
@Override
public void update(int lowTemperature, String weather, int highTemperature) {
System.out.println("北京天气:" + weather + ",最低气温:" + lowTemperature + ",最高气温:" + highTemperature);
}
}
class GuangdongTv implements Observer {
@Override
public void update(int lowTemperature, String weather, int highTemperature) {
System.out.println("广东天气:" + weather + ",最低气温:" + lowTemperature + ",最高气温:" + highTemperature);
}
}
通知过程
观察者注册:
WeatherData weatherData=new WeatherData();
BeijingTv beijingTv=new BeijingTv();
weatherData.registerObserver(beijingTv);
当数据改变时,会通知各个观察者更新数据:
weatherData.setWeatherData(10,"晴转多云",20);
控制数据推送频率
天气数据每分钟都会发生变化,会造成被观察者不断的向观察者推送数据,更该采取的措施是每个半小时或者当温度变化0.5°时再进行推送更新。
通过添加标志量changed,可以控制数据的更新:
private boolean changed = false;
public void setChanged(boolean changed) {
this.changed = changed;
}
public void dataChanged() {
if (changed) {
notifyObserver();
}
changed = false;
}
当需要更新时,需要先设置为true:
weatherData.setChanged(true);
weatherData.setWeatherData(10,"晴转多云",20);
观察者模式实现2——拉取数据
上面的实现都是被观察者向观察者推送数据,其实观察者也可利用getter()方法从被观察者中取出自己想要的数据
被观察者接口
不需要改变:
interface Subject {
void registerObserver(Observer o);
void unregisterObserver(Observer o);
void notifyObserver();
}
观察者接口
修改Observer的update方法不添加参数:
interface Observer {
void update();
}
被观察者实例
修改WeatherData在notifyObserver()方法中不传递参数给observer的update()方法,同时为属性添加getter()方法:
public class WeatherData implements Subject {
private int lowTemperature;
private String weather;
private int highTemperature;
private ArrayList<Observer> observers;
public WeatherData() {
observers = new ArrayList<>();
}
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void unregisterObserver(Observer o) {
int i = observers.indexOf(o);
if (i > 0) {
observers.remove(o);
}
}
@Override
public void notifyObserver() {
for (int i = 0; i < observers.size(); i++) {
Observer observer = observers.get(i);
observer.update();
}
}
public int getLowTemperature() {
return lowTemperature;
}
public String getWeather() {
return weather;
}
public int getHighTemperature() {
return highTemperature;
}
public void dataChanged() {
notifyObserver();
}
public void setWeatherData(int lowTemperature, String weather, int highTemperature) {
this.lowTemperature = lowTemperature;
this.weather = weather;
this.highTemperature = highTemperature;
dataChanged();
}
}
观察者实例
观察者实例内部维护被观察者接口,通过构造方法传入,当需要更新数据时进行数据类型转换再调用gettter()方法获取
class BeijingTv implements Observer {
private Subject subject;
public BeijingTv(Subject subject) {
this.subject = subject;
this.subject.registerObserver(this);
}
@Override
public void update() {
if (subject instanceof WeatherData) {
WeatherData weatherData= (WeatherData) subject;
System.out.println("天气:" +weatherData.getWeather()+",最低气温:" + weatherData.getLowTemperature() + ",最高气温:" + weatherData.getHighTemperature());
}
}
}
通知过程
被观察者作为参数传入观察者内部注册:
WeatherData weatherData=new WeatherData();
BeijingTv beijingTv=new BeijingTv(weatherData);
当数据改变时,会通知各个观察者更新数据:
weatherData.setWeatherData(10,"晴转多云",20);
应该选哪个?
- 推送数据实现:将获取到的数据推送给观察者,由观察者从中进行筛选出需要的数据进行更新
- 拉取数据实现:由观察者自己请求相关数据,但这时可能被观察者并没有准备好对应的数据
根据需要选择相应的实现。