观测者模式定义了对象之间的一对多依赖,当一个对象状态发生改变时,其依赖者便会收到通知并做相应的更新。其原则是:为交互对象之间松耦合。以松耦合方式在一系列对象之间沟通状态,我们可以独立复用主题(Subject)/可观测者(Observable)和观测者(Observer),即只要遵守接口规则改变其中一方并不会影响到另一方。这也是其主要的设计原则。下面是一个简单的气象站发送天气信息给布告板,然后布告板把天气信息显示在板上的例子。
首先先建立三个接口,主题(Subject)、观测者(Observer)和显示内容(DisplayElement),分别代表气象站、布告板信息接收和布告板信息显示。
/**
* 主题
*/
public interface Subject {
// 观察者注册
public void registerObserver(Observer o);
// 删除观察者
public void removeObserver(Observer o);
// 当主题有内容更新时调用,用于通知观察者
public void notifyObserver();
}
/**
* 观察者
*/
public interface Observer {
// 当气象站观测的天气发生变化时,主题会把参数值传给观察者
public void update(float temp);
}
/**
* 用于布告板显示
*/
public interface DisplayElement {
// 在显示布告板上显示的操作
public void display();
}
接下来是实现气象站()和布告板()了。
/**
* 气象站实现主题,发布气象信息(气温)
*/
public class WeatherStation implements Subject{
private ArrayList observers;
private float temp;
public WeatherStation() {
// 加个ArrayList存放所有注册的Observer对象;
observers = new ArrayList<>();
}
@Override
public void registerObserver(Observer o) {
// 当新的观察者注册时添加进来
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
// 当观察者取消注册时去除该观察者
int i = observers.indexOf(o);
if (i>=0) {
observers.remove(i);
}
}
@Override
public void notifyObserver() {
// 更新状态,调用Observer的update告诉观察者有新的信息
for (int i = 0; i < observers.size(); i++) {
Observer observer = (Observer) observers.get(i);
observer.update(temp);
}
}
/*
* 此方法用于气象站收到的数据,并且调用更新使数据实时通知给观察者
*/
public void setMeasurements(float temp){
this.temp = temp;
System.out.println("气象站测量的温度为:" + temp + "℃");
notifyObserver();
}
}
/**
* 布告板上的状态显示
*/
public class ConditionDisplay implements Observer,DisplayElement{
private float temp;
private Subject weatherStation;
public ConditionDisplay(Subject weatherStation) {
// 构造时需要间主题/被观察者对象作为注册之用
this.weatherStation = weatherStation;
weatherStation.registerObserver(this);
}
@Override
public void display() {
// 将数据显示在布告板上
System.out.println("布告板显示当前温度为:" + temp + "℃");
}
@Override
public void update(float temp) {
// 接受来自主题/被观察者(气象站)的数据
this.temp = temp;
display();
}
}
测试结果
/**
* 天气观测站
*/
public class WeatherObserver {
public static void main(String[] args) {
// 首先创建一个主题/被观察者
WeatherStation weatherStation = new WeatherStation();
// 创建观察者并将被观察者对象传入
ConditionDisplay conditionDisplay = new ConditionDisplay(weatherStation);
// 设置气象站模拟收到的气温数据
weatherStation.setMeasurements(25);
weatherStation.setMeasurements(24);
weatherStation.setMeasurements(23);
}
}
JAVA内置观察者模式
可以使用java内置的观察者模式,这样就无需自己写Subject和Observer类了,在java.util包下继承的Observable和实现Observer类即可。其修改后的代码如下:
/**
* 继承java内置的被观察者,因此不再需要注册和删除了
*/
public class WeatherStationN extends Observable{
private float temperature;
public WeatherStationN() {
// 由于继承了Observable,它已经创建了一个Vector来存放Observer对象的容器,所以此处不用再建立ArrayList
}
/*
* 此方法用于气象站收到的数据,并且调用更新使数据实时通知给观察者
*/
public void setMeasurements(float temp){
this.temperature = temp;
System.out.println("气象站测量的温度为:" + temp + "℃");
// 更新强调用表示有状态更新
setChanged();
notifyObservers(temperature);
}
}
/**
* 实现java内置Observer接口
*/
public class ConditionDisplayN implements java.util.Observer,DisplayElement{
private Observable observable;
private float temp;
public ConditionDisplayN(Observable observable) {
// 构造器需要Observable作为参数
this.observable = observable;
observable.addObserver(this);
}
@Override
public void display() {
// 将数据显示在布告板上
System.out.println("布告板显示当前温度为:" + temp + "℃");
}
@Override
public void update(Observable o, Object arg) {
// 当被观察者有更新使触发
if (o instanceof WeatherStationN) {
this.temp = (float) arg;
display();
}
}
}
测试运行结果
/**
* 天气观测站
*/
public class WeatherObserver {
public static void main(String[] args) {
// 首先创建一个主题/被观察者
WeatherStationN weatherStationN = new WeatherStationN();
// 创建观察者并将被观察者对象传入
ConditionDisplayN conditionDisplayN = new ConditionDisplayN(weatherStationN);
// 设置气象站模拟收到的气温数据
weatherStationN.setMeasurements(30);
weatherStationN.setMeasurements(25);
weatherStationN.setMeasurements(20);
}
}
注意:
Observer是一个接口,而Observable是一个类,在使用时必须继承它,因此在继承Observable时就无法再继承其他超类了,因为Java毕竟不支持多重继承。且在Observable更新前,即notifyObservers()或notifyObservers(Object arg)前要先调用setChange()标记更新状态