文章已同步至掘金:https://juejin.cn/post/6844903921211670536
欢迎访问😃,有任何问题都可留言评论哦~
有人说这两个模式其实是一个模式。这句话一半对,一半错吧。它们有类似的地方,不过也不能说完全一致。先来一张图,这张图解释了观察者模式和发布订阅模式在流程上的一些区别:
阐述二者的模型:
-
观察者模式里,观察者(Observer)直接订阅(subscribe)主题(Subject),而当主题被激活的时候,会触发(fire)观察者里的事件。
-
发布订阅模式里,订阅者(Subscriber)通过监听(on)事件总线(Event Bus)里的事件,当事件总线里的事件被触发(emit)的时候,订阅者将会执行相应的操作。而这里需要注意的是,事件总线里的事件是通过发布者(Publisher)进行发布(publish)和 通知事件总线 触发 的。
观察者模式
如上图所示,既然发布/订阅是比观察者模式多了一些东西,那么我们就先来看观察者模式。
观察者模式 :定义对象间一种一对多的依赖关系
简单的就是说:A 对象依赖 B 对象的某个状态变化来执行一些逻辑,那么 A 就需要观察 B 的某个状态,B 会在这个状态发生改变时通知 A,并且可以把描述这个状态的数据给 A
举个例子:
假设你是个学渣,你同桌是个学霸,今天要考试了,卷子发下来,你几乎一道题都不会做,于是你对同桌说:你做完了给我抄一下,你同桌做完了,拿胳膊肘捅你一下,把试卷往你这边挪。
你是观察者,你同桌是被观察者,你观察的状态是你同桌做完了,你同桌的试卷是你要的数据,这个状态发生的时候你要处理的逻辑是抄答案。
抄过作业的都知道,你不可能一直盯着你同桌或者一直问他写完没吧?在程序中,如果对象 A 一直不停的去获取 B 的某个状态来看有没有发生改变,这不是观察者模式
什么是一对多的依赖关系?
你同桌是学霸,你同桌前后左右都跟你一样是学渣,你们都得抄他的,不然全不及格。就是这样。
在程序中,就是一个按钮被点击了,程序打开一个页面。所谓的监听这个按钮的点击事件就是一种观察者模式
撸段代码
//被观察者
class Subject{
constructor(){
this.arr = [] //存储观察者
this.state = "开心..." //存储被观察者状态
}
attach(unit){
this.arr.push(unit)
}
//改变状态
setState(newState){
this.state = newState
this.arr.forEach(unit=>unit.update(newState))
}
}
//观察者
class Observer{
constructor(name){
this.name = name
}
update(newState){
console.log(this.name,"状态:",newState);
}
}
let s = new Subject("宝宝")
let o1 = new Observer("我")
let o2 = new Observer("你")
s.attach(o1)
s.attach(o2)
console.log(s.state); //开心...
s.setState("不开心..."); //我 状态: 不开心... 你 状态: 不开心...
console.log(s.state); //不开心...
发布订阅模式
在发布订阅模式中:消息的发送方,叫做发布者(publishers),消息不会直接发送给特定的接收者,叫做订阅者。
意思就是发布者和订阅者不知道对方的存在。需要一个第三方组件,叫做中间件,它将订阅者和发布者串联起来。
举个例子:
你在淘宝上看中了一个电脑,然后你找了个代购,你跟他说如果找到这个电脑就买给你,这个代购路子野有很多渠道,电脑到货后这些渠道商会告诉这个代购,电脑到货了,然后代购买下来发给你
这个才是 发布订阅模式
而观察者模式呢?
你在淘宝上看上了一个电脑,但是没货了,于是你给店家说到货了告诉你,店家说好的,到货了告诉你。
这个是观察者模式
在程序中,比方说一个应用区分登录状态和未登录状态的UI,很多不同的页面都可以实现登录的逻辑,在登录成功后,所有当前被打开的页面都需要更新UI。这种情况就可以使用发布/订阅模式,在某个页面登录成功后发布登录成功的事件,有中间件将这个事件分发给其他页面更新UI,
注:其他页面关心的只是登录状态改变这件事,而不关心用户具体在哪个页面登录的
撸代码:
let e = {
arr:[],
on(fn){
this.arr.push(fn)
},
emit(){
this.arr.forEach(fn=>fn())
}
}
e.on(()=>{
console.log("哈哈 1");
})
e.on(()=>{
console.log("哈哈 2");
})
//调一次打印一次
e.emit() //哈哈 1 哈哈 2
e.emit() //哈哈 1 哈哈 2
区别:
这两种模式区别可以简单归结为是观察状态还是事件。
观察者模式中:
- 状态发布者维护了一个观察者的列表,明确的知道有哪些观察者存在,将状态变化直接通知给观察者
- 状态的观察者也明确的知道自己观察的状态是描述的哪一个对象
- 甚至需要这种相互知道的关系来处理逻辑(比如需要明确知道哪一个按钮被点击,处理对应的逻辑)
发布订阅模式:
- 事件的发布者只发布事件,不关心这个事件被谁获取了,通常将事件发给一个中间件,由中间件再去分发事件
- 事件的订阅者只关心事件本身,不关心这个事件是谁发布的,通常在中间件中去注册观察某个事件
- 中间件中去维护事件类别对应的订阅者列表,当收到事件后,去对应列表中通知订阅者们
应用场景
按如下原则判断:
是否观察的是状态(明确知道状态源)
如果被观察和观察者双方都明确知道对方,那就观察者模式,否则就是发布订阅模式
一对一或者一对多的关系
这个事件或者状态只有一个发布者,两种都可以用,再参考第一条
多对多的关系
首先所谓多对多的关系基本就可以确定传递的是事件,而不是状态,因为不同对象不应该发布相同的状态,所以不要犹豫就选发布/订阅
评论区