观察者模式
观察者模式一般有观察者和被观察者。举个例子:大家在学校上自习的时候,等老师走了有些人会玩手机、吃零食、交头接耳找隔壁妹妹聊天,但是被老师发现可就不好了,所以大家想了一个招,让坐在最后排的同学帮忙“放风”,老师一来就给大家一个手势通知大家,大家就继续装好好学生(哈嘿)。
这其实就是一个典型的观察者模式,“放风”的同学是被观察者,玩手机、吃零食的同学是观察者,大家都在观察“放风”同学的手势,一旦老师来了,被观察者就会通知大家。
好了,让我们看看 UML 建模是如何定义的。
观察者模式定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
UML结构图如下:
Subject类是主题,它把所有对观察者对象的引用文件存在了一个集合里,每个主题都可以有任何数量的观察者。它是一个抽象主题,提供了一个可以增加和删除观察者对象的接口。
Observer类是抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。
ConcreteSubject类是具体主题,将有关状态存入具体观察者对象,在具体主题内部状态改变时,给所有登记过的观察者发出通知。
ConcreteObserver是具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协同。
发布订阅模式
举个生活中的例子,比如我们想要订阅一份国家地理杂志,一般需要我们先向邮局申请(付钱),告诉邮局我要订阅这份杂志,苦等数日杂志终于印刷好了,这个时候我们不会直接跑到印刷厂里去,而是等印刷厂将杂志送给邮局,然后邮局才会慢吞吞地将杂志送到家(推模式),如果你实在等不及了跑到邮局直接取杂志,恭喜你学会了“拉模式”。
用专业术语来解释发布订阅模式:
订阅者把自己想订阅的事件注册到调度中心,当该事件触发时候,发布者发布该事件到调度中心(顺带上下文),由调度中心统一调度订阅者注册到调度中心的处理代码。
在发布订阅模式里发布者并不会直接通知订阅者,换句话说发布者和订阅者彼此互不感知。
那发布者和订阅者如何交流呢?答案是通过中间的调度中心。
- 发布者将消息发送给调度中心,告诉它你帮我把消息放到 Topic1中。
- 订阅者告诉调度中心,我需要订阅 topic1,你帮我留意一下。
- 当有消息来了,订阅者可以采取拉模式或者推模式来获取消息。
有态度的总结
话不多说,先上一张图:
从表面上看:
- 观察者模式里只有两个角色:观察者和被观察者。
- 发布订阅模式里有三种角色:发布者、订阅者、调度器(第三者)。
往更深层次讲:
- 观察者和被观察者是松耦合的关系。
- 发布者和订阅者则完全不存在耦合。
从使用层面上讲:
- 观察者模式经常用于单个应用内部。
- 发布订阅模式更多是一种跨应用的模式(cross-application pattern),比如我们常用的消息中间件Kafka 等。
综上:观察者模式和发布订阅模式本质上都有发布订阅的思想,但是又有一定的区别,所以我们不能将二者完全等同起来。