设计模式系列二之观察者模式(Observer)

什么是观察者模式:让你的对象知悉现状

观察者模式是软件设计模式中的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常呼叫各个观察者所提供的方法来实现。

  • 摘自维基百科

简单来说,观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态是,它的所有依赖者都会收到通知并自动更新。在观察者模式中,一对多依赖中的”一”一般被称作主题(Subject),而”多”则被称作观察者(Observer)

观察者的类图

观察者模式的优点:松耦合

当2个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。

观察者模式提供了一种对象设计,让主题和观察者之间松耦合。为什么呢?

  1. 关于观察者的一切,主题只知道观察者实现了某个接口(也就是Observer接口),主题不需要知道观察者具体是谁、做了些什么或其他任何细节;
  2. 任何时候我们都可以添加新的观察者。因为主题唯一依赖的东西是一个实现Observer接口的对象列表,所以我们可以随时增加观察者。事实上,在运行时我们可以用新的观察者取代现有的观察者,主题不会受到任何影响。同样的,也可以在任何时候删除某些观察者。
  3. 有新类型的观察者出现时,主题的代码不需要修改。假如我们有个新的具体类需要当观察者,我们不需要为了兼容新类型而修改主题的代码,所要做的就是在新的类里实现观察者接口,然后注册为观察者即可。主题不在乎别的,它只会发送通知给所有实现了观察者接口的对象。

我们可以独立地复用主题或者观察者。如果我们在其他地方需要使用主题或者观察者,可以轻易地复用,因为二者并非紧耦合。

改变主题或者观察者中的一方,并不会影响另一方。因为二者是松耦合的,所以只要他们之间的接口仍被遵守,我们就可以自由地改变他们。

观察者模式的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.github.archerda.designpattern.observer.custom;
/**
* 主题接口;
* Create by archerda on 2017/08/08.
*/
public interface Subject {
/**
* 观察者注册;
*/
boolean registerObserver(Observer observer);
/**
* 观察者取消注册;
*/
boolean removeObserver(Observer observer);
/**
* 当主题状态改变时,这个方法会被调用,用于通知所有的观察者;
*/
boolean notifyObservers();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.github.archerda.designpattern.observer.custom;
/**
* 观察者接口;
* Create by archerda on 2017/08/08.
*/
public interface Observer {
/**
* 接受主题的通知;
* 当主题状态改变时,主题会把这些状态当做方法的参数,传递给观察者;
*
* @param temp 温度;
* @param humidity 湿度;
* @param pressure 压强;
*/
void update(float temp, float humidity, float pressure);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.github.archerda.designpattern.observer.custom;
import java.util.ArrayList;
/**
* 主题具体实现
* Create by archerda on 2017/08/08.
*/
public class WeatherData implements Subject {
private ArrayList<Observer> observers = new ArrayList<>();
private float temperature;
private float humidity;
private float pressure;
@Override
public boolean registerObserver(Observer observer) {
return observers.add(observer);
}
@Override
public boolean removeObserver(Observer observer) {
int i = observers.indexOf(observer);
if (i > 0) {
observers.remove(i);
return true;
}
return false;
}
@Override
public boolean notifyObservers() {
for (Observer observer : observers) {
observer.update(temperature, humidity, pressure);
}
return true;
}
public void measurementsChanged() {
notifyObservers();
}
public void setMeasurements(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.github.archerda.designpattern.observer.custom;
import com.github.archerda.designpattern.observer.DisplayElement;
/**
* 具体观察者实现;
* Create by archerda on 2017/08/08.
*/
public class CurrentConditionDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherSubject;
public CurrentConditionDisplay(Subject weatherSubject) {
this.weatherSubject = weatherSubject;
weatherSubject.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
display();
}
@Override
public void display() {
System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");
}
}

JDK内置的观察者模式

Java API有内置的观察者模式。java.util包内包含最基本的Observable类和Observer接口,这和我们的Subject接口和Observer接口很类似,Observable类和Observer接口使用上更方便,因为许多功能都已经事先准备好了。你甚至可以使用push或pull的方式传送数据。

java.util.Observable的不足

如同我们所见,Observable是一个”类”而不是一个”接口”,更糟糕的是,它甚至没有实现一个接口。不幸的是,java.util.Observable的实现有许多问题,限制了它的使用和复用。

我们从设计原则可以知道这不是好事。

首先,因为Observable是一个类,你必须设计一个类继承它。如果某类想同时具有Observable类和另一个父类的行为,就会陷入两难,毕竟Java不支持多继承。这就限制了Observable的复用能力(而复用能力不正是我们使用模式最原始的动机么?)。

再者,因为没有Observable接口,所以你无法建立自己的实现,和Java内置的Observer API搭配使用,也无法将java.util的实现换成另一套做法。

观察者模式在Spring中的应用-Spring事件驱动模型

事件机制的实现需要三部分,事件源事件事件监听器。下面我们分别来看。

事件:ApplicationEvent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public abstract class ApplicationEvent extends EventObject {
private static final long serialVersionUID = 7099057708183571937L;
private final long timestamp;
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
public final long getTimestamp() {
return this.timestamp;
}
}

ApplicationEvent继承自JDK的EventObject,Spring中所有的事件都需要继承自ApplicationEvent,并且通过source取得事件源。ApplicationEvent的实现类ApplicationContextEvent表示ApplicationContext的容器事件。

事件监听器:ApplicationListener

1
2
3
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}

ApplicationListener继承自JDK的EventListener,所有的Spring监听器都要实现这个接口,这个接口只有一个onApplicationEvent()方法,该方法接受一个ApplicationEvent或其子类对象作为参数。在方法体中,可以通过不同对Event类的判断来进行相应的处理。当事件触发时,所有的监听器都会收到消息,如果你需要对监听器的接收顺序有要求,可以实现该接口的一个实现SmartApplicationListener,通过这个接口可以指定监听器接收事件的顺序。

事件源:ApplicationContext

ApplicationContext是Spring中的全局容器,翻译过来是”应用上下文”的意思,它用来负责读取bean的配置文档、管理bean的加载、维护bean之间的依赖关系,可以说是负责bean的整个生命周期,再通俗一点讲就是我们平时所说的IoC容器。

ApplicationContext作为一个事件源,需要显示地调用publishEvent()方法,传入一个ApplicationEvent对象作为参数,每当ApplicationContext发布ApplicationEvent时,所有的ApplicationListener就会被自动触发。

ApplicationEvent接口继承了ApplicationEventPublisher接口,后者有一个很重要的方法:

1
2
3
public interface ApplicationEventPublisher {
void publishEvent(ApplicationEvent event);
}

我们常用的ApplicatonContext都继承自 AbstractApplicationContext,像我们平时常见的ClassPathXmlApplicationContext、XmlWebApplicationContext也都是继承自它,AbstractApplicationContext是ApplicationContext接口的抽象实现类,在该类中实现了publishEvent()方法。

1
2
3
4
5
6
7
8
9
10
11
@Override
public void publishEvent(ApplicationEvent event) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
getApplicationEventMulticaster().multicastEvent(event);
if (this.parent != null) {
this.parent.publishEvent(event);
}
}

在这个方法中,我们看到一个getApplicationEventMulticaster(),这就要牵涉到另一个类 ApplicationEventMulticaster。

ApplicationEventMulticaster

ApplicationEventMulticaster 属于事件广播器,它的作用是把 ApplicationContext 发布的 Event 广播给所有的监听器。

在 AbstractApplicationContext 中有一个 applicationEventMulticaster 的成员变量,提供了监听器Listener的注册方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext, DisposableBean {
/** Helper class used in event publishing */
private ApplicationEventMulticaster applicationEventMulticaster;
/**
* Add beans that implement ApplicationListener as listeners.
* Doesn't affect other listeners, which can be added without being beans.
*/
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String lisName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(lisName);
}
}
}

Spring事件模型总结

从上面可以看出,Spring的事件驱动模型使用的是 观察者模式。事件监听器 ApplicationListener 是观察者,ApplicationContext 是主题。

参考文档: