最近在看了一篇《不好意思,观察者模式跟发布订阅模式就是不一样》的文章之后对于这两个模式产生了比较浓厚的兴趣。不过奈何我的水平有限,看完那篇文章还是不能理解。不过在和朋友讨论之后,我想我应该是弄懂了。所以特地记下一篇笔记,以便回头翻阅的时候能够想起来。如果理解有误,欢迎在下方评论指出,一起讨论!
概述
有人说这两个模式其实是一个模式。我想这句话的对错对半分吧。它们有类似的地方,不过也不能说完全一致。先来一张图,这张图解释了观察者模式
和发布订阅模式
在流程上的一些区别:
左边是观察者模式,右边是订阅发布模式。
简单阐述二者的模型:
观察者模式里,观察者(Observer)直接订阅(subscribe)主题(Subject),而当主题被激活的时候,会触发(fire)观察者里的事件。
订阅发布模式里,订阅者(Subscriber)通过监听(on)事件总线(Event Bus)里的事件,当事件总线里的事件被触发(emit)的时候,订阅者将会执行相应的操作。而这里需要注意的是,事件总线里的事件是通过发布者(Publisher)进行发布(publish)和 通知事件总线 触发 的。
注:事件总线也有说法叫为调度中心。本质上是一样的。不过因为写Vue时候习惯用Event Bus来说了,所以本文的调度中心皆以事件总线称呼。
所以事件总线本身不独自发布和触发事件,它会借由发布者来操作。这是跟观察者模式有着比较大的区别的地方。
当然只看这两张图和上面的解释,应该还是无法很好的理解。下面这张图能把流程讲得更清楚点。
这个例子可以理解为这样:左边是微信里的微商-顾客
之间的关系。右边是商家-淘宝-顾客
之间的关系。
观察者模式:顾客关注了微商的商品,微商会记住顾客关注的商品,一旦上新就直接 私聊 通知所有关注这个商品的顾客。这里的顾客就相当于观察者,这里的微商就相当于主题。
订阅发布模式:顾客通过淘宝(APP或者网站)关注了商家的商品,商家一旦上新就通过淘宝(APP或者网站)向关注了它的顾客 群发 消息。这里的顾客就是订阅者,这里的淘宝就是事件总线,这里的商家就是发布者。
所以可以看出,观察者模式的模型跟发布订阅模型里,差距就差在有没有一个中央的事件总线。如果有这个事件总线,我们就可以认为是个发布订阅模型。如果没有,那么就可以认为是个观察者模型。因为其实它们都实现了一个关键的功能:发布事件-订阅事件并触发事件。
下面用代码简单解释一下。
观察者模式
由于最近在学习TypeScript,所以下面的代码也会用TypeScript来书写。
我们先写一个定义观察者和主题的文件。
1 | // observer-pattern.ts |
于是在调用的时候,是这样调用的:
1 | import * as op from './observer-pattern' |
经过上述调用,subjects触发观察者订阅的click事件,observer.subject
的值将会变为Done
(原先为click
)。
订阅发布模式
接下来我们来实现一些订阅发布模式。订阅发布模式最关键的地方就在于中间的Event Bus
部分。它接管着事件总线的订阅和发布。
1 | // pubsub.ts |
可以看出在这里的EventBus
和观察者模式里的Subject
几乎一致对吧。但是需要注意的是,最后一行里,我们export default new EventBus()
,所以我们在项目里不同的地方import
它,都会指向同一个Event Bus
实例,这样的话就可以起到一个事件总线的作用了。它不在乎谁来监听,谁来发布。只要有人监听了,就把它放进监听队列中。只要有人发布了事件,就从相应的监听队列中触发回调。不过所有相关的事件都必须经过Event Bus
这个实例,而不能越过它直接由发布者通知监听者。
再次祭出这张图
所以在订阅发布模型里,发布者或者订阅者的身份已经被弱化。发布者可以在任何时候发布事件,而订阅者可能只是一个回调函数。而最关键的事件总线部分,则是发布订阅模型的核心。
如果你用过Vue的Event Bus
,相信不会陌生。接下来我们来用用我们刚才写的简单的Event Bus
。
1 | import bus from './pubsub.ts' |
所以你可以看到,这个事件总线是可以单独抽离出来的。如果要把我们这个文件丢到一个现有的项目里也是完全没问题的。
其实在写Vue组件通信的时候,你如果用到了Event Bus
的话,也是一样的。在全局声明一个new Vue()
做Event Bus
总线,然后在不同的组件里只要引入了这个事件总线,就能订阅或者发布不同的消息。这个就是一个非常典型的订阅发布模型。
而如果只是Vue的父子组件通信,子组件用的是this.$emit
来触发事件,父组件用的是this.$on
这样的方式去订阅事件,那么你可以认为这个就是一个简单的观察者模型。因为它们之间的联系是紧密耦合的。
总结
不管是观察者模式也好,订阅发布模式也好,关键在于实现了在某个特定时间触发某个特定事件,从而触发监听这个特定事件的组件进行相应操作的功能。这个设计模式在很多时候非常有用。平时只是用到了它,但是没有深入去看看如何实现,这次借由这个机会把二者的关系和区别记录下来,也算是给自己加深了印象。
本文的代码你可以在我的学习仓库FE-Learning找到。如有错误欢迎指出!
参考资料
https://www.zcfy.cc/article/observer-vs-pub-sub-pattern-hacker-noon
http://blog.zxbing0066.com/design-patterns/2016/09/12/observer-pattern.html
https://juejin.im/post/5af05d406fb9a07a9e4d2799
https://www.cnblogs.com/weebly/p/5279952.html