观察者模式定义了对象之间的一对多关系,多个对象依赖于一个有状态对象的状态值,当对象状态改变时,他所有的依赖者都会受到通知并自动更新
观察者模式一对关系中,一的一方为subject
,多的一方为observer
。subject
为一个接口或是抽象类,自己设计过程中,使用接口有利于类的灵活扩展(Java不支持多重继承)。subject
类型应该具有以下接口
public interface Subject {
public void regist(Observer ob);
public void removeOb(Observer ob);
public void notifyObs(Object object);
}
其中,regist
方法用来为Obsever
对象注册观察关系,removeOb用来移除观察关系,notifyObs
则是将subject
的状态改变通知到诸多观察者。
而Observer接口则要实现以下接口
public interface Observer {
public void update(Object subject);
}
此方法将在subject
对象状态发生改变时被调用,将变化后的对象传递到Observer
中。以整个对象为参数的update
需要Subject
子类实现状态的getter
方法以供Observer
的子类进行状态的读取。另一种方式是以所有的状态值作为参数,这种情况下在代码层面上会造成参数的冗余(当观察者只关心部分状态值时)
下面我们看一个具体的实现,此实例参考HeadFirst一书。
现在有一个天气数据的主题,我们有好多个布告板用来显示天气数据。天气数据用WeatherData
来表示,而布告板用CurrentConditionDisplay
来表示,以下是代码:
public class WeatherData implements Subject {
private ArrayList<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
public WeatherData() {
this.observers = new ArrayList<>();
}
@Override
public void regist(Observer ob) {
if (this.observers.indexOf(ob) == -1) {
this.observers.add(ob);
}
}
@Override
public void removeOb(Observer ob) {
if (this.observers.indexOf(ob) > 0) {
this.observers.remove(ob);
}
}
@Override
public void notifyObs(Object obj) {
observers.forEach(ob -> {
ob.update(this);
});
}
public void measurementChanged() {
notifyObs(null);
}
public void setMeasurement(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
this.measurementChanged();
}
}
当天气数据发生改变时会调用setMeasurement
函数,具体是怎么样调用不用关心。
CurrentConditionDisplay
实现如下:
public class CurrentConditionDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private float pressure;
private Subject subject;
public CurrentConditionDisplay(Subject subject) {
this.subject = subject;
this.subject.regist(this);
}
@Override
public void update(Object obj) {
if (obj instanceof WetherData) {
WetherData wetherData = (WetherData) obj;
this.temperature = wetherData.getTemperature();
this.humidity = wetherData.getHumidity();
this.pressure = wetherData.getPressure();
}
this.display();
}
@Override
public void display() {
System.out.println("temperatrue : "
+ this.temperature
+ "\n"
+ "humidity : "
+ this.humidity
+ "\n"
+ "pressure : "
+ this.pressure
+ "\n");
}
}
代码中实现的DisplayElement
接口声明了一个display
函数
public interface DisplayElement {
public void display();
}
总体而言,当测量结果发生改变时,notifyObs
方法会被调用,此方法为每一个观察者调用它的update
方法, 并传递对象本身以传递状态数据,观察者代码中使用getter方法获取各种状态值以供显示。
当有新的观察者需要依赖天气数据时,只需要实现Observer
接口,并声明一个以Subject
实例为参数的构造函数,并进行注册。注册过程会将自身的引用交给Subject
的对象,当Subject
对象状态发生改变时会为每一个Observer
调用update
方法,以通知Observer
做出改变。
上述代码中,我们在 CurrentConditionDisplay
类型中保留了一个Subject
的引用,从上述过程中看,这里的Subject
的引用只是在构造函数中进行了初始化,并没有在其他地方用到。其实这里保留这个引用是为了能够在Observer
方面进行解除观察关系,只是代码中没有实现而已。