(一) 什么是观察者模式
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新.
(二) 观察者模式的四个角色
· 抽象主题角色:一个接口或抽象类,定义了主题的基本功能(添加,删除观察者,把更新信息发送给观察者).
· 抽象观察者角色:为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
· 具体主题角色:一个具体的主题,在集体主题的内部状态改变时,向所有登记过的观察者发出通知。
· 具体观察者角色:实现抽象观察者接口,观察者必须注册具体主题,以便接收更新.
(三) 观察者模式应用场景
现在有一个气象站,很多气象软件都会获取气象站的数据来显示给自己的用户,并且在气象站更新数据时,气象软件会受到更新信息并更新自己的数据.
现在,让我们来构建观察者模式.在这里,气象站是一个主题,而气象软件则是观察者,观察气象站数据.
1, 定义一个抽象主题接口
public interface Observerable {
//添加一个观察者
public void registerObserver(Observer o);
//删除一个观察者
public void removeObserver(Observer o);
//将更新信息发送给所有观察者
public void notifyObserver();
}
2、定义一个抽象观察者接口
public interface Observer {
//当气象观测值改变时,主题会把这些状态值当作方法的参数,传送给观察者。
//当主题调用notifyObservers()方法时,观察者的update()方法会被回调。
public void update(float temp, float humidity, float pressure);
}
3、定义主题者WeatherData,实现了Observerable接口,对Observerable接口的三个方法进行了具体实现,同时有一个List集合,用以保存注册的观察者,等需要通知观察者时,遍历该集合即可。
public class WeatherData implements Observerable {
//记录该主题的观察者
private List<Observer> observers = new ArrayList<Observer>();
//气象数据
private float temperature;
private float humidity;
private float pressure;
/**
* @Description:注册观察者
* @param o
*/
@Override
public void registerObserver(Observer o) {
//将观察者添加到注册表中
observers.add(o);
}
/**
* @Description:注销观察者
* @param o
*/
@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if(i>=0){
observers.remove(i);
}
}
/**
* @Description:把更新的信息发送给所有观察者
*/
@Override
public void notifyObserver() {
//遍历所有观察者,把更新信息推给所有观察者
for(int i=0;i<observers.size();i++){
Observer observer = observers.get(i);
observer.update(temperature, humidity, pressure);
}
}
/**
* @Description:气象站更新自己的信息
* @param temperature
* @param humidity
* @param pressure
*/
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
//更新完自己的信息后,要推送给所有观察者
notifyObserver();
}
}
4 , 定义具体观察者,建立布告板.
public class CurrentConditionsDisplay implements Observer{
private float temperature;
private float humidity;
private float pressure;
private Observerable weatherData;
//在创建观察者时需要主题对象,以用来注册或注销
public CurrentConditionsDisplay(Observerable weatherData){
this.weatherData = weatherData;
}
//根据主题--气象站的信息更新自己的信息
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
this.pressure = pressure;
display();
}
//显示信息
public void display(){
System.out.println("temperature:"+temperature+" ,humidity:"+humidity+" ,pressure:" +pressure);
}
}
测试一下我们的观察者模式
public static void main(String[] args) {
//创建一个气象站
WeatherData weatherData = new WeatherData();
//创建两个观察者,
CurrentConditionsDisplay observer1 = new CurrentConditionsDisplay(weatherData);
CurrentConditionsDisplay observer2 = new CurrentConditionsDisplay(weatherData);
//注册观察者
weatherData.registerObserver(observer1);
weatherData.registerObserver(observer2);
//气象站更新数据了
weatherData.setMeasurements(1, 2, 3);
System.out.println("------注销一个观察者-----------");
//将一个观察者注销
weatherData.removeObserver(observer1);
//再次更新数据
weatherData.setMeasurements(2, 3, 4);
}
可以看到,当观察者在主题中注册之后就可以收到主题更新的信息,而一旦注销之后,就不会收到主题的信息.
但是有一个问题,那就是并不是所有的观察者都需要主题的所有信息,他们只想自己拉取他们想了解的那部分信息,而不是由主题将所有的信息都推给观察者.而且使用拉的方式获取信息会使得你的功能更容易扩展,就算以后主题又添加了一个属性,只需要设置相应的getter就可以.
下面,我们使用java内置的观察者模式来实现我们的功能
public class WeatherData2 extends Observable {
private float temperature;
private float humidity;
private float pressure;
//不用再记住观察者
//private List<Observer> observers = new ArrayList<Observer>();
public WeatherData2(){
}
/**
* @Description:气象站更新自己的信息
* @param temperature
* @param humidity
* @param pressure
*/
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
//更新完自己的信息后,要推送给所有观察者
notifyObservers();
}
//生成getter方法,以让观察者拉取
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
//实现java内置观察者java.util.Observer;
public class CurrentConditionsDisplay2 implements Observer {
Observable weatherData;
private float temperature;
private float humidity;
//在创建观察者时需要主题对象,以用来注册或注销
public CurrentConditionsDisplay2(Observable weatherData){
this.weatherData = weatherData;
//由观察者自己决定是否关注主题
weatherData.addObserver(this);
}
//由自己来拉取所需要的信息
public void update(Observable o, Object arg) {
WeatherData2 weatherData2 = (WeatherData2)o;
this.temperature = weatherData2.getTemperature();
this.humidity = weatherData2.getHumidity();
display();
}
//显示信息
public void display(){
System.out.println("temperature:"+temperature+" ,humidity:"+humidity);
}
}