最近看redux都有发布订阅,绕不过去,打不过就加入 原创:(建超还在桥代码)
1.观察者模式(发布-订阅)
概念:观察者模式定义了一种依赖关系,当某一个对象的状态发生变化,其它依赖这个对象的对象都会受到影响
一个不知道当讲不当讲的例子理解观察者模式:
1.1 初级的观察者模式代码 (low 狗)
院子里有一个小偷,几只狗,小偷行动,狗就开始叫 ,这里 狗叫依赖小偷的行动
<script>
// 代码
class Thief { //创建一个小偷的类
constructor () {
}
action () { //小偷有行动的方法
//行动调用的是实例化狗的方法
dog1.call()
dog2.call()
dog3.call()
}
}
class Dog { //创建一个 狗 类
call () {
console.log("wang wang")
}
}
let dog1 = new Dog() //实例化狗1
let dog2 = new Dog() //实例化狗2
let dog3 = new Dog() //实例化狗3
let thief = new Thief()
//最后我们调用小偷的行动方法
thief.action() // 3次 wang wang
</script>
上面的代码中,小偷调用action方法的时候,其内部会分别调用每条狗的call方法。这段代码有个明显的缺点,对象耦合,不方便维护,假如需求中增加了一条狗,此时如何更改代码呢?就必须给两个类中全部追加方法,明显很low
<script>
// 代码
class Thief {
constructor () {
}
action () { // 这里小偷是被观察者
dog1.call()
dog2.call()
dog3.call()
dog4.call() // 要在action这个动作里追加新增实例化狗4的叫的方法
}
}
class Dog { //创建一个 狗 类
call () {
console.log("wang wang")
}
}
//狗子们是观察者 (观察)
let dog1 = new Dog()
let dog2 = new Dog()
let dog3 = new Dog()
let dog4 = new Dog() //新增加的狗
let thief = new Thief()
//最后我们调用小偷的行动方法
thief.action()
</script>
1.2 中级的观察者模式代码 ( 菜狗 )
<script>
//具有储存实例化狗子的能力 追加狗子的时候不用在这个小偷里追加
class Thief { //新的厉害小偷
constructor () { //给小偷的实例化对象追加list 状态 用来储存方法
this.list = [] //这里的this指的是实例化的小偷 list 里面之后会被放进去很多方法
}
//调用这个方法时把call扔进实例化的list里
subscrible (call) { //英语直译就是订阅 我老读不读 经典王氏音标: se be se crea bool
this.list.push(call)
}
//调用这个方法时调用list里面的call方法
publish () { //publish直译过来就是发布 出版
for (let i = 0; i < this.list.length; i++ ){
this.list[i]()
}
}
//这个action其实就只调用publish方法
action () {
this.publish()
}
}
//继续捏个狗子模具(创造一个狗子的类)
class Dog {
call () {
alert("旺") //狗子发出了叫声
}
}
//根据狗子模具捏出两只菜狗子
let dog1 = new Dog()
let dog2 = new Dog()
//根据升级的小偷模具模具捏出一个小偷
let thief = new Thief()
//然后我们调用小偷订阅方法(subscrible),并且把两只狗子叫的方法传进去, 这样thief的list里就有dog1.call()方法和dag2.call()
thief.subscrible( dog1.call)
thief.subscrible( dog2.call)
// 然后小偷行动 , 会调用小偷的发布publish方法 执行list 里的函数 调用dog1.call()
thief.action() //alert 了两次
</script>
仔细阅读代码,我们首先重新定义了Thief类,并为其添加了subscribe方法、publish方法、list属性,并重新定义了dog。然后我们用thief的subscribe方法收集dog的call方法,将其添加到小偷的list属性中。当小偷调用action时,其内部调用publish方法,publish会遍历执行list数组中的方法。这个时候就相对比较优雅了,增加狗子的时候只需要调用thief中subscrible直接改变,并没有改变thief的方法
但是其实并不够完善,因为可能有两个或者多个小偷(不仅仅可能时多个订阅者,也可能会存在发布者)
如果新增加一个实例化小偷 代码应该时这样的
class Thief {
constructor () {
this.list = []
}
subscrible (call) {
this.list.push(call)
}
publish () {
for (let i = 0; i < this.list.length; i++ ){
this.list[i]()
}
}
action () {
this.publish()
}
}
class Dog {
call () {
alert("旺")
}
}
let dog1 = new Dog()
let dog2 = new Dog()
let thief = new Thief()
let secondThief = new Thief() //追加了第二个发布者
thief.subscrible( dog1.call)
thief.subscrible( dog2.call)
secondThief.subscrible( dog1.call ) //使用发布者的收集订阅方法 收集订阅者的方法 或者可以直接额收集订阅者
secondThief.subscrible( dog2.call )
、
thief.action()
secondThief.action()
1.2 高级的观察者模式代码 ( 优雅 )
<script>
// 将观察者模式抽离出来
class Publish {
constructor() {
this.list = [] //存放订阅者的地方
}
subscrible(call) { //收集订阅者
this.list.push(call)
}
publish() {
for (let i = 0; i < this.list.length; i++) {
this.list[i]()
}
}
}
let pubsub = new Pubsub() //新的发布订阅
class Dog {
call() {
alert("wang")
}
}
class Thief {
constructor() {
}
action() {
pubsub.publish()
}
}
let thief = new Thief()
let thief1 = new Thief()
let dog1 = new Dog()
pubsub.subscrible(dog1.call)
let dog2 = new Dog()
pubsub.subscrible(dog2.call)
let dog3 = new Dog()
pubsub.subscrible(dog3.call)
</script>
仔细阅读源码,我们分别添加了分别添加了thief1和dog3,依然能够实现小偷偷东西,狗会叫的功能,并且不会去修改thief和dog内部的代码,实现了对象之间的解耦。
观察上图,第三版中图片第一张图多了一个pubsub,我们用一个卫星来代替pubsub,这个版本也比较好维护,添加删除thief或者dog都不会影响到对象。我们在前端应用中使用的redux和vuex都运用了观察者模式,或者叫做订阅者模式,其运行原理也如上图。