背景
需求案例
- 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)。
- 需要设计开放型API,便于其他第三方也能接入气象站获取数据。
- 提供温度、气压和湿度的接口
-
基本介绍
定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。
主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。观察者模式UML
主题(Subject)具有注册和移除观察者、并通知所有观察者的功能,主题是通过维护一张观察者列表来实现这些操作的。
观察者(Observer)的注册功能需要调用主题的 registerObserver() 方法。
应用示例
需求
气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)。
需求分析
代码实现
```java //接口, 让WeatherData 来实现 public interface Subject {
public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObservers(); }
```java//观察者接口,具体观察者来实现public interface Observer {public void update(float temperature, float pressure, float humidity);}
public class CurrentConditions implements Observer {// 温度,气压,湿度private float temperature;private float pressure;private float humidity;// 更新 天气情况,是由 WeatherData 来调用,我使用推送模式public void update(float temperature, float pressure, float humidity) {this.temperature = temperature;this.pressure = pressure;this.humidity = humidity;display();}// 显示public void display() {System.out.println("***Today mTemperature: " + temperature + "***");System.out.println("***Today mPressure: " + pressure + "***");System.out.println("***Today mHumidity: " + humidity + "***");}}
public class BaiduSite implements Observer {
// 温度,气压,湿度
private float temperature;
private float pressure;
private float humidity;
// 更新 天气情况,是由 WeatherData 来调用,我使用推送模式
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
// 显示
public void display() {
System.out.println("===百度网站====");
System.out.println("***百度网站 气温 : " + temperature + "***");
System.out.println("***百度网站 气压: " + pressure + "***");
System.out.println("***百度网站 湿度: " + humidity + "***");
}
}
/**
* 该类是核心
* 1. 包含最新的天气情况信息
* 2. 含有 观察者集合,使用ArrayList管理
* 3. 当数据有更新时,就主动的调用 ArrayList, 通知所有的(接入方)就看到最新的信息
* @author Administrator
*
*/
public class WeatherData implements Subject {
private float temperatrue;
private float pressure;
private float humidity;
//观察者集合
private ArrayList<Observer> observers;
//加入新的第三方
public WeatherData() {
observers = new ArrayList<Observer>();
}
public float getTemperature() {
return temperatrue;
}
public float getPressure() {
return pressure;
}
public float getHumidity() {
return humidity;
}
public void dataChange() {
//调用 接入方的 update
notifyObservers();
}
//当数据有更新时,就调用 setData
public void setData(float temperature, float pressure, float humidity) {
this.temperatrue = temperature;
this.pressure = pressure;
this.humidity = humidity;
//调用dataChange, 将最新的信息 推送给 接入方 currentConditions
dataChange();
}
//注册一个观察者
@Override
public void registerObserver(Observer o) {
// TODO Auto-generated method stub
observers.add(o);
}
//移除一个观察者
@Override
public void removeObserver(Observer o) {
// TODO Auto-generated method stub
if(observers.contains(o)) {
observers.remove(o);
}
}
//遍历所有的观察者,并通知
@Override
public void notifyObservers() {
// TODO Auto-generated method stub
for(int i = 0; i < observers.size(); i++) {
observers.get(i).update(this.temperatrue, this.pressure, this.humidity);
}
}
}
public class Client {
public static void main(String[] args) {
//创建一个WeatherData
WeatherData weatherData = new WeatherData();
//创建观察者
CurrentConditions currentConditions = new CurrentConditions();
BaiduSite baiduSite = new BaiduSite();
//注册到weatherData
weatherData.registerObserver(currentConditions);
weatherData.registerObserver(baiduSite);
//测试
System.out.println("通知各个注册的观察者, 看看信息");
weatherData.setData(10f, 100f, 30.3f);
System.out.println("=========华丽分割========");
weatherData.removeObserver(currentConditions);
weatherData.setData(12f, 200f, 10.3f);
}
}

JDK中Observable 类源码分析
进入Observable,其中addObserver相当于注册一个观察者,deleteObserver是移除一个观察者,notifyObserver是通知观察者们,与之前的案例类似

进入Observer,发现它是一个接口,里面定义了update方法
模式角色分析
Observable 的作用和地位等价于 我们前面讲过 Subject
Observable 是类,不是接口,类中已经实现了核心的方法 ,即管理 Observer 的方法 add… delete … notify…
Observer 的作用和地位等价于我们前面讲过的 Observer, 有 update
Observable 和 Observer 的使用方法和前面讲过的一样,只是 Observable 是类,通过继承来实现观察者模式
监听者模式(观察者的另一种形态)
监听器模式通常包含三个角色:事件源、事件对象、事件监听器。
如果观察者模式中的类名和方法对照改一下,并不改变业务逻辑,我们来看看是啥效果。
比如,将Observer改名为Listener,将其update方法改为onClick();将Subject的实现类ConcreteSubject改名为ListenerSupport,将registerObserver方法更名为addListener……
定义一个事件对象Event,用来传递事件信息:
public class Event {
private String data;
private String type;
Event(String data, String type) {
this.data = data;
this.type = type;
}
// 省略getter/setter
}
原来主题的订阅对象Observer改名为Listener之后,成为监听器:
public interface Listener {
void onClick(Event event);
}
为监听器提供一个实现类,当然在实践中也可以采用匿名类创建的方式:
public class ListenerA implements Listener {
@Override
public void onClick(Event event) {
System.out.println("触发事件,type:" + event.getType() + ",data:" + event.getData());
}
}
原来的主题ConcreteSubject对照ListenerSupport成为事件管理器:
public class ListenerSupport {
private List<Listener> listeners = new ArrayList<>();
public void addListener(Listener listener) {
listeners.add(listener);
}
public void triggerEvent(Event event) {
for (Listener listener : listeners) {
listener.onClick(event);
}
}
}
测试
public class EventTest {
public static void main(String[] args) {
Listener listener = new ListenerA();
ListenerSupport listenerSupport = new ListenerSupport();
listenerSupport.addListener(listener);
listenerSupport.triggerEvent(new Event("dataA", "typeA"));
}
}
通过上面的对照代码,我们可以看出,即便业务逻辑不变,经过重命名的观察者模式已经变为监听器模式了。而它们的对照关系是:事件源对照ConcreteSubject(主题)、事件对象对照update方法的Object、事件监听器对照ConcreteObserver(订阅者)。这也再次证明了所说的“监听器模式是观察者模式的另一种形态”。
观察者模式和监听器模式对比

通过对比可以发监听器模式的优势是:在很多场景中,通知中附带了一些必不可少的其他信息,而事件Event可以对这些信息进行封装,使它本身拥有了多态的特性。每个事件对象就可以包含不同的信息。从这个层面来说,事件监听器模式是对观察者模式进行了进一步的抽象。
Spring中的监听
观察者模式的经典应用算是Spring事件驱动模型了,它便是基于观察者模式实现的,同时也是项目中最常见的事件监听器。
Spring中观察者模式包含四个角色:事件、事件监听器、事件源、事件管理。
事件:ApplicationEvent是所有事件对象的父类。ApplicationEvent继承自jdk的EventObject,所有的事件都需要继承ApplicationEvent,并且通过source得到事件源。Spring 提供了很多内置事件,比如:ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent、RequestHandledEvent。
事件监听器:ApplicationListener,也就是观察者,继承自jdk的EventListener,该类中只有一个方法onApplicationEvent,当监听的事件发生后该方法会被执行。
事件源:ApplicationContext,ApplicationContext是Spring的核心容器,在事件监听中ApplicationContext可以作为事件的发布者,也就是事件源。因为ApplicationContext继承自ApplicationEventPublisher。在ApplicationEventPublisher中定义了事件发布的方法:publishEvent(Object event)。
事件管理:ApplicationEventMulticaster,用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把Applicationcontext发布的Event广播给它的监听器列表。
