Observer 是一种“发布-订阅”类的模式,是比较常用的设计模式之一,提到“发布-订阅”,有很多框架和工具都是基于“发布-订阅”模式实现的,我们举一反三一下,比如我们常用的各种 MQ 消息中间件,生产者和消费者之间就是一种“发布-订阅”的关系,再如 zookeeper,节点的修改方和节点的监听方同样形成一种“发布-订阅”的关系。“发布-订阅”类的模式有一个非常明显的优点就是它能够使主题及其观察者松散耦合,我们来看在一个非常典型交易场景中 Observer 模式可以解决什么样的问题。
下图是一个很常规的线上交易流程,我们选择好自己要买的商品后加入到购物车,然后进入结算页查看本次交易的应付金额和优惠信息等,最后付款完成交易。
交易订单完成之后,要做很多的后续工作,比如我们给用户发送一个通知,告知用户刚刚完成了一笔新订单的支付;其次要对这笔交易进行计费和扣点;如果本次交易有优惠,系统要自动核销优惠;可能会有一些其他的系统关心本次交易的状态,这样再交易完成后要通知到相关系统。还有很多其他的动作就不一一列举了,第一版本的代码,我们形成了这样的结构:
class OrderMessageResolver implements MessageResolver {
DeductSupport duductSupport;
ChargingSupport chargingSupport;
MQSender mqSender;
void resolve(){
// ...
// 推进订单完成
finishOrder();
// 计费
charge();
// 扣点
deduct();
// 发消息通知用户
mqSender.send();
// ...
}
void finishOrder() {
//...
}
void charge() {
//...
chargingSupport.charge();
//...
}
void deduct() {
//...
deductSupport.doDeduct ();
//...
}
void noticeUser() {
//...
mqSender.send();
//...
}
}
结构非常简单,就是在接受到订单交易完成的消息后,一步一步的将需要完成的工作执行下去。在后续的迭代中,我们渐渐发现了这种结构的几大缺点:
- resolve方法里面做了太多的逻辑,导致代码的可读性极差,维护起来也相当麻烦。
- 有很多逻辑并不是OrderMessageResolver这个类应该负责的,已经超过了这个类的职责,这与面向对象的设计理念是相违背的。
- 很难扩展,随着业务的发展,订单完成之后肯定还会有更过的动作,后面再添加业务逻辑的时候会非常困难,对这个类的修改是相当危险的,很难知道新加的逻辑会不会对之前的逻辑产生影响,对于的交易系统来说,一旦产生一些故障,损失是非常惨重的。
针对以上这些问题,我们引入 Observer 模式来解决,下图是 JDK 对 Observer 模式支持的类图:
在本例中,订单的完成状态就是被观察的 Subject,而完成后的各个动作我们把它定义为不同的 Observer。重构之后的结构如下:
class OrderMessageResolver extends Observable implements MessageResolver {
void init() {
addObserver(ChargingObserver);
addObserver(DeductObserver);
addObserver(NoticeUserObserver);
}
void resolve() {
// 推进订单完成
finishOrder();
// 通知 Observer
notifyOvservers();
}
void finishOrder() {
//...
}
}
/**
* Observer 基类
*/
abstract class AbstractOrderFinishObserver implements Observer {
void update(Observable, args) {
param = parse(args);
try {
update(param);
} catch(...) {
//...
}
}
abstract void update(param);
}
/**
* 扣点 Observer
*/
class DeductObserver extends AbstractOrderFinishObserver {
DeductSupport duductSupport;
void update(param) {
//...
deductSupport.doDeduct ();
//...
}
}
/**
* 计费 Observer
*/
class ChargingObserver extends AbstractOrderFinishObserver {
ChargingSupport chargingSupport;
void update(param) {
//...
chargingSupport.charge();
//...
}
}
在主题对象 OrderMessageResolver 中添加 Observer 列表(这里只列出了计费、扣点两个 Observer,其他的类似,在此就不一一列举了),订单完成之后通知各个 Observer。本例中使用的是 JDK 中自带的对 Observer 模式支持的工具类,原理和代码都很简单,大家可以自己去看源码,不过使用这种方式实现 Observer 模式有一个缺点,就是主题对象需要继承 JDK 的 Observable 类,对于单继承的 java 语言来说会有一定的限制,当然我们可以选择自己实现 Observer 模式,也非常简单。
重构后我们提升了代码的可读性,在 OrderMessageResolver 中,推进订单完成后它的职责就已经完成了,后续的逻辑都不是它的职责范围,只需要把订单完成的状态通知到各个 Observer,这样逻辑就不会耦合在一起。其次,重构后的代码具有良好的扩展性,后续再有订单完成之后的业务逻辑只需要添加一个 Observer,满足 OCP 原则。我们再使用 Observer 模式时需要注意,实现 Observer 模式时应该尽量避免出现串联通知,就是 Observer 本身又变成了一个主题,它又会通知其他的 Observer,这会产生过于复杂的设计,并使调试变得很难。而且我们并不推荐应用程序一开始就引入 Observer 模式,当硬编码的通知已经足够的时候,强行引入会增加设计的复杂度,我们应该在真正需要的时候,再把设计改进为使用 Observer,这并不困难~