设计模式:观察者与发布订阅模式

278 阅读4分钟

观察者模式

描述:

观察者模式(Observer Pattern)由被观察者和观察者组成,观察者可以是多个,被观察者维护着多个观察者,如添加或删除观察者;当被观察着数据变化时,会通过广播的方式通知维护的每一个观察者(即调用观察者提供的回调函数)。

概要:

观察者模式:定义了对象间一种一对多的依赖关系,当目标对象 Subject 的状态发生改变时,所有依赖它的对象 Observer 都会得到通知

举个栗子:

小美通过自己的努力吸引了2个追求者.小美每次发朋友圈必定会得到两个追求者的回应.小美与两个追求者的关系形成了观察者模式.小美是被观察者,两个舔狗是观察者.小美发朋友圈这个动作会触发舔狗的回应

// 被观察者
class ABeautifulGirl {
  TIAN_GOU_LIST = [] // 舔狗们
  // 添加观察者
  attractTianGou(...tianGous) {
    Array.prototype.push.apply(this.TIAN_GOU_LIST, tianGous)
  }
  // 删除观察者
  kickOffTianGou(tianGou) {
    const index = this.TIAN_GOU_LIST.indexOf(tianGou)
    this.TIAN_GOU_LIST.splice(index, 1)
  }
  // 女神技能:发朋友圈(发布通知)
  sendPost(data) {
    this.TIAN_GOU_LIST.forEach(tianGou => {
      tianGou.callback.call(tianGou, data)
    })
  }
}
// 观察者
class TianGou {
  constructor(name, fn) {
    this.name = name
    this.lick = fn
  }
  callback(data) {
    console.log(
      `我是${this.name}今天女神发朋友圈说她${data},我要给女神发信息:`
    )
    this.lick()
  }
}
// 定义两个观察者
const tiangou1 = new TianGou('tiangou1', () => {
  console.log('多喝热水')
})
const tiangou2 = new TianGou('tiangou2', () => {
  console.log('请一定要多喝热水呀')
})
// 小美通过自己的努力有了吸引别人关注的能力
const xiaomei = new ABeautifulGirl()
xiaomei.attractTianGou(tiangou1, tiangou2)
// 小美发了个朋友圈
xiaomei.sendPost('肚子疼')

image.png

再举个栗子

舔狗舔了数次始终没有的到小美的青睐, 这次他学会了主动, 他准备再勾搭一个女神, 于是他想办法加了小丽的微信, 开始了同时对两人的观察.

class ABeautifulGirl {
  constructor(name) {
    this.name = name
  }
  TIAN_GOU_LIST = []
  sendPost(data) {
    console.log(`${this.name},发个朋友圈`)
    this.TIAN_GOU_LIST.forEach(tianGou => {
      tianGou.callback.call(tianGou, data)
    })
  }
}
// 观察者:(主动)关注或取消关注
class TianGou {
  constructor(name, fn) {
    this.name = name
    this.lick = fn
  }
  subscribe(publish) {
    var sub = this
    var alreadyExists = publish.TIAN_GOU_LIST.some(function (item) {
      return item === sub
    })
    // 如果女神的好友名单中没有这个人,则加入进去
    if (!alreadyExists) publish.TIAN_GOU_LIST.push(sub)
    return this
  }
  unsubscribe(publish) {
    var sub = this
    publish.TIAN_GOU_LIST = publish.TIAN_GOU_LIST.filter(function (item) {
      return item !== sub
    })
    return this
  }
  callback(data) {
    console.log(
      `    ${this.name} 看到女神发朋友圈说她 ${data} ,我要给女神发信息:`
    )
    this.lick()
  }
}
// 小美 & 小丽
const xiaomei = new ABeautifulGirl('xiaomei')
const xiaoli = new ABeautifulGirl('xiaoli')
// 两个舔狗
const tiangou1 = new TianGou('tiangou1', () => {
  console.log('    多喝热水')
})
const tiangou2 = new TianGou('tiangou2', () => {
  console.log('    请一定要多喝热水呀')
})
// 舔狗主动订阅女神们的消息
tiangou1.subscribe(xiaomei).subscribe(xiaoli)
tiangou2.subscribe(xiaomei)
// 小美小丽肚子疼
xiaomei.sendPost('肚子疼')
xiaoli.sendPost('腰子疼')

image.png

优缺点:

  优点明显:降低耦合,两者都专注于自身功能;   缺点也很明显:所有观察者都能收到通知,无法过滤筛选;

发布订阅模式

Publisher && Subscriber

描述:

舔狗们订阅了很多女神, 但大都无疾而终, 舔狗中有一只聪明狗, 姑且就叫它二哈吧, 二哈首先意识到, 女神们生病的时候只是发 [多喝热水] 是没有用的, 还要在早上对女神说早上好, 下班了对女生下班好, 去输液了要对女神说说输的的想你的夜. 二哈开始行动了, 于是二哈学习了发布订阅模式.

概要:

发布订阅模式:基于一个事件(主题)通道,希望接收通知的对象 Subscriber 通过自定义事件订阅主题,被激活事件的对象 Publisher 通过发布主题事件的方式通知各个订阅该主题的 Subscriber 对象。 发布订阅模式与观察者模式的不同,“第三者” (事件中心)出现。目标对象并不直接通知观察者,而是通过事件中心来派发通知。

举个栗子:

const GirlsHub = function () {
  this.grils = {}
  this.on = function (name, cb) {
    if (this.grils[name]) {
      this.grils[name].push(cb)
    } else {
      this.grils[name] = [cb]
    }
  }
  this.trigger = function (name, ...arg) {
    if (this.grils[name]) {
      this.grils[name].forEach(eventListener => {
        eventListener(...arg)
      })
    }
  }
}
let girls = new GirlsHub()
girls.on('morring', () => {
  console.log('对小美说早安')
})
girls.on('morring', () => {
  console.log('对小丽说早安')
})
girls.on('morring', () => {
  console.log('对小美丽说早安')
})
girls.on('noon', () => {
  console.log('对小美说午安')
})
girls.on('noon', () => {
  console.log('对小丽说午安')
})
girls.on('noon', () => {
  console.log('对小美丽说午安')
})
girls.on('evening', () => {
  console.log('对小美说晚安')
})
girls.on('evening', () => {
  console.log('对小丽说晚安')
})
girls.on('evening', () => {
  console.log('对小美丽说晚安')
})
girls.trigger('morring')
girls.trigger('noon')
girls.trigger('evening')

image.png

学会之后,二哈化被动为主动, 每到时辰就开始给女神们发消息, 本以为能够收获女神们的芳心,到后来发现,自己被一半人都拉黑了.

看来功力还不够深厚啊.需要学习以下其他的设计模式来优化以下方案。

优缺点:

  优点:解耦更好,细粒度更容易掌控;

  缺点:不易阅读,额外对象创建,消耗时间和内存

两种模式的关联和区别

发布订阅模式更灵活,是进阶版的观察者模式,指定对应分发。

  1. 观察者模式维护单一事件对应多个依赖该事件的对象关系;
  2. 发布订阅维护多个事件(主题)及依赖各事件(主题)的对象之间的关系;
  3. 观察者模式是目标对象直接触发通知(全部通知),观察对象被迫接收通知。发布订阅模式多了个中间层(事件中心),由其去管理通知广播(只通知订阅对应事件的对象);
  4. 观察者模式对象间依赖关系较强,发布订阅模式中对象之间实现真正的解耦。