设计模式——观察者模式的思考

观察者模式是用于建立对象与对象之间的依赖关系,即当一个对象(目标对象)发生改变时,所依赖它的对象(观察者对象)也会随之改变,目标对象和观察者对象之间往往是一对多的关系,并且观察者对目标对象是单向依赖的,也就是说目标对象无需关心有没有观察者,观察者始终处于被动状态,典型的观察者模式除了目标对象(Subject)和观察者对象(Observer),还有他们的实现对象ConcreteSubject和ConcreteObserver,他们的模式结构图如下:

Subject:目标对象,当事件发生后负责通知具体的观察者,其实就是循环调用观察者所持有的接口。
Observer:观察者对象,负责具体的事件的处理,该接口能够有效的分离任务,不同的任务就是不同的子类,能够很好的描述业务。

推模型和拉模型

可以通过两种方式来实现观察者模式分别是拉模型和推模型
拉模型:Subject把自身通过update方法传递给观察者,只要通知到观察者事件发生即可,至于什么时候获取变更内容可以观察者自行决定。
推模型:Subject主动向观察者推送事件变更的详细信息,推送的信息通常都是目标对象的全部或部分数据,观察者模式只能被动接受。
从模式结构图可以看到传统的观察者就是拉模型,相对于拉模型,推模型需要知道观察者需要的详细数据,一旦观察的数据变更,原有的观察者对象将难以复用。

观察者模式实战

观察者模式的使用场景很多,比如说在用户支付订单完成后,就有很多步骤需要被执行如:
1.增加用户对应的积分
2.短信或邮件通知用户下单成功
3.同步订单到其他平台
4….
可以发现支付订单的后续步骤需要符合以下两点:
1.支付完成与后续步骤之间是一对多的关系
2.多个步骤之间是相互独立的关系,没有依赖

模式结构图

根据上面的描述可以设计出以下的模式结构图:

OrderPaymentHandlerObserver
OrderPaymentHandlerObserver是观察者的抽象接口,里面包含两个方法filterEventeventHandler,filterEvent方法用于判断该观察者对象是否处理该事件,eventHandler则是具体处理方法,可以看到OrderPaymentHandlerObserver的三个子类相互独立,各司其职,很好体现了单一职责原则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface OrderPaymentHandlerObserver {

/**
* 判断是否处理该Event
* @param orderDetail 订单明细
* @return 是否支持
*/
boolean filterEvent(OrderPaymentDetailDTO orderDetail);

/**
* Event处理
* @param orderDetail 订单明细
*/
void eventHandler(OrderPaymentDetailDTO orderDetail);

}

OrderPaymentSubject
OrderPaymentSubject是目标对象的抽象接口,负责通知具体的观察者

1
2
3
4
5
6
7
8
9
public interface OrderPaymentSubject {

/**
* 通知观察者
* @param orderDetail 支付明细
*/
void notifyObservers(OrderPaymentDetailDTO orderDetail);

}

OrderPaymentConcreteSubject
OrderPaymentConcreteSubject是对目标接口的实现,主要实现了对观察者的通知,所谓通知观察者就是循环调用自己所持有观察者的接口,当支付完成后,OrderPaymentConcreteSubject就会循环调用notifyObservers接口,完成后续的所有步骤。

1
2
3
4
5
6
7
8
9
10
11
12
public class OrderPaymentConcreteSubject implements OrderPaymentSubject {
private List<OrderPaymentHandlerObserver> observers;

@Override
public void notifyObservers(OrderPaymentDetailDTO orderDetail) {
for (OrderPaymentHandlerObserver observer : observers) {
if (observer.filterEvent(orderDetail)) {
observer.eventHandler(orderDetail);
}
}
}
}

Spring管理观察者

OrderPaymentSubject作为目标对象持有所有的观察者,如果利用Spring的IOC把所有的Observer都注入到该类,再新增Observer,不需要修改任何代码,可以实现完完全全的解耦。具体的实现就是让OrderPaymentConcreteSubject实现ApplicationListener<ContextRefreshedEvent>接口,等待Spring初始化完成后,然后利用BeanFactoryUtils初始化所有需要的观察者集合,下面是对OrderPaymentConcreteSubject的修改

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
@Component
public class OrderPaymentConcreteSubject implements OrderPaymentSubject,ApplicationListener<ContextRefreshedEvent> {

private List<OrderPaymentHandlerObserver> observers;

@Override
public void notifyObservers(OrderPaymentDetailDTO orderDetail) {
for (OrderPaymentHandlerObserver observer : observers) {
if (observer.filterEvent(orderDetail)) {
observer.eventHandler(orderDetail);
}
}
}

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
initObserver(event);
}

private void initObserver(ContextRefreshedEvent event) {
Map<String, OrderPaymentHandlerObserver> observerMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(
event.getApplicationContext(), OrderPaymentHandlerObserver.class, true, false);
this.observers = Collections.unmodifiableList(new ArrayList<>(observerMap.values()));
}

}

与发布-订阅模式的区别

发布-订阅模式属于广义上的观察者模式,笔者认为他们主要区别如下:
1.在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者模式不知道对方的存在,他们通过消息代理进行通信。
2.观察者模式是同步的,发布-订阅模式往往都是异步的,如消息队列。

感兴趣的粒度

推荐阅读:观察者模式“感兴趣”的粒度控制

参考