观察者模式对象间建立一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。
观察者模式可在主题处push或者pull数据,push方式被认为更正确的方式。以下第一个实例中用push,第二个用pull。
java类图
java代码
// 主题接口
public interface Subject {
void register(Observer observer);
void remove(Observer observer);
void notifyObserver();
}
// 主题
public class WeatherData implements Subject {
private ArrayList<Observer> observers;
private int temperature;
private int humidity;
private int pressure;
public WeatherData() {
observers = new ArrayList<>();
}
@Override
public void register(Observer observer) {
if (!observers.contains(observer)) {
observers.add(observer);
}
}
@Override
public void remove(Observer observer) {
int index = observers.indexOf(observer);
if (index >= 0) {
observers.remove(index);
}
}
@Override
public void notifyObserver() {
for (Observer observer : observers) {
// 通知所有订阅的观察者,主动将数据推送给观察者
observer.update(temperature, humidity, pressure);
}
}
public void setData(int temperature, int humidity, int pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
// 数据改变后会调用notifyObserver方法
notifyObserver();
}
}
// 展示接口,与模式无关
public interface Display {
void display();
}
// 观察者接口
public interface Observer {
void update(int temperature, int humidity, int pressure);
}
// 天气情况展示板
public class WeatherDisplay implements Observer, Display {
// 方便取消订阅
private Subject subject;
private int temperature;
private int humidity;
private int pressure;
public WeatherDisplay(Subject subject) {
this.subject = subject;
// 新建展示板时,订阅主题
subject.register(this);
}
@Override
public void display() {
System.out.println("temperature=" + temperature + ", humidity=" + humidity + ", pressure=" + pressure);
}
@Override
public void update(int temperature, int humidity, int pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
// 主题变化时调用update,display展示内容
display();
}
}
// 天气改变展示板
public class ChangeDisplay implements Observer, Display {
// 方便取消订阅
private Subject subject;
private int temperature;
private int humidity;
private int preTemperature;
private int preHumidity;
public ChangeDisplay(Subject subject) {
this.subject = subject;
subject.register(this);
}
@Override
public void display() {
System.out.println("temperatureChange=" + displayCal(temperature, preTemperature) + ", humidityChange=" + displayCal(humidity, preHumidity));
}
public String displayCal(int now, int pre) {
int difference = now - pre;
if (difference > 0) {
return "↑";
} else if (difference < 0) {
return "↓";
} else {
return "-";
}
}
@Override
public void update(int temperature, int humidity, int pressure) {
preTemperature = this.temperature;
preHumidity = this.humidity;
this.temperature = temperature;
this.humidity = humidity;
display();
}
}
// 测试类
public class Test {
public static void main(String[] args) {
new Test().test();
}
private void test() {
WeatherData weatherData = new WeatherData();
WeatherDisplay weatherDisplay = new WeatherDisplay(weatherData);
ChangeDisplay changeDisplay = new ChangeDisplay(weatherData);
weatherData.setData(10, 23, 2);
weatherData.setData(8, 24, -1);
}
}
注:
主题直接将数据推送给观察者,属于push方式
观察者模式定义了对象间的一对多关系
主题用一个共同的接口来更新观察者
观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口
有多个观察者时,不可以依赖特定的通知顺序
java自带观察者接口
类图
java代码
// 主题
public class WeatherData extends Observable { // 采用继承的方式
private int temperature;
private int humidity;
private int pressure;
public void setData(int temperature, int humidity, int pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
// 需要设置标记才能通知观察者
setChanged();
// 通知观察者并传递一个object对象
notifyObservers("push object");
}
public int getTemperature() {
return temperature;
}
public int getHumidity() {
return humidity;
}
public int getPressure() {
return pressure;
}
}
// 展示接口
public interface Display {
void display();
}
// 天气情况展示板
public class WeatherDisplay implements Observer, Display {
// 方便取消订阅
private Observable observable;
private int temperature;
private int humidity;
private int pressure;
public WeatherDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void display() {
System.out.println("temperature=" + temperature + ", humidity=" + humidity + ", pressure=" + pressure);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherData) {
WeatherData weatherData = (WeatherData) o;
// 主题推送自己的引用,观察者需要自己去主题中获取数据
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
this.pressure = weatherData.getPressure();
System.out.println("Object arg is " + arg);
display();
}
}
}
// 天气改变展示板
public class ChangeDisplay implements Observer, Display {
// 方便取消订阅
private Observable observable;
private int temperature;
private int humidity;
private int preTemperature;
private int preHumidity;
public ChangeDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void display() {
System.out.println("temperatureChange=" + displayCal(temperature, preTemperature) + ", humidityChange=" + displayCal(humidity, preHumidity));
}
public String displayCal(int now, int pre) {
int difference = now - pre;
if (difference > 0) {
return "↑";
} else if (difference < 0) {
return "↓";
} else {
return "-";
}
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherData) {
WeatherData weatherData = (WeatherData) o;
preTemperature = this.temperature;
preHumidity = this.humidity;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
System.out.println("Object arg is " + arg);
display();
}
}
}
// 测试类
public class Test {
public static void main(String[] args) {
new Test().test();
}
private void test() {
WeatherData weatherData = new WeatherData();
WeatherDisplay weatherDisplay = new WeatherDisplay(weatherData);
ChangeDisplay changeDisplay = new ChangeDisplay(weatherData);
weatherData.setData(10, 23, 2);
weatherData.setData(8, 24, -1);
}
}
注:
主题将自己的引用推送给观察者,观察者主动从主题中获取数据,属于pull方式
java.util.Observable是一个类,无法自定义实现,而且也无法再继承其他类。
参考文章
Head First 设计模式