观察者模式简介
定义
观察者模式,又称发布-订阅模式。它定义对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,所有依赖于它的对象都将得到通知。
现实例子
现今各种直播平台盛行,多数人平时会观看一些直播。假如我们在某个直播平台上有喜欢的主播,但是她的上线时间不是很固定,为了不错过她的直播就要时不时瞄一眼手机看她有没有上线,这样十分浪费时间与精力。所以我们可以使用直播平台的订阅功能去订阅某个主播,当主播上线的时候会给所有订阅她的人推送一条消息,这样我们就可以及时知道主播已经上线去打开直播而不用浪费时间。
实现思路
- 指定好一个发布者(直播平台)
- 给发布者添加一个缓存列表,用于存放订阅者或者待执行的回调函数(订阅某一主播的所有观众名单)
- 最后发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函 数(遍历名单,挨个推送上线提醒)
核心代码
// 模拟直播平台 发布中心
const eventCenter = {
// 缓存列表 存放订阅者(需订阅者提供一个update方法)
clientList: {},
// 订阅 增加key标识来区不同的主播
subscribe: function (key, observer) {
if (!this.clientList[key]) {
this.clientList[key] = [];
}
// 将订阅者添加进缓存列表
this.clientList[key].push(observer);
},
// 发布 支持传递不同的key 来区分不同主博,同时支持传递额外参数
publish: function () {
// 获取发布的key,即不同的主播
let key = [].shift.call(arguments),
obs = this.clientList[key];
// 如果没有订阅者就 停止发布
if (!obs || obs.length === 0) {
return false;
}
// arguments 是发布时额外带上的参数
for (var i = 0, ob; ob = obs[i++];) {
ob.update.apply(ob, arguments);
}
}
};
// 模拟三个观众
const ob1 = {
name: '看官1',
update: function (arg) {
console.log(`${this.name}收到了${arg}`);
}
}
const ob2 = {
name: '看官2',
update: function (arg) {
console.log(`${this.name}收到了${arg}`);
}
}
const ob3 = {
name: '看官3',
update: function (arg) {
console.log(`${this.name}收到了${arg}`);
}
}
// 订阅
eventCenter.subscribe('aa', ob1);
eventCenter.subscribe('aa', ob2);
eventCenter.subscribe('bb', ob3);
// 发布
eventCenter.publish('aa', '主播a上线的提醒');
eventCenter.publish('bb', '主播b上线的提醒');
// 输出结果
看官1收到了主播a上线的提醒
看官2收到了主播a上线的提醒
看官3收到了主播b上线的提醒
以上是一个简单的观察者模式的例子,一些前端响应式框架的实现更是离不开 观察者模式的核心思想。
redux中的体现
redux 是react官方推荐的状态管理仓库。用法暂不赘述,下面是redux的极简实现
let createStore = (reducer) => {
let state;
// 缓存列表 类似上个例子的 clientList 存放所有状态变化时的响应函数
let listeners = [];
let getState = () => state;
// 发布 类似上个例子中的publish方法, 在修改state的同时 遍历缓存列表 执行响应函数
let dispath = (action) => {
// 修改state
state = reducer(state, action);
// 执行所有的监听函数
listeners.forEach((l) => l())
}
// 订阅 类似上个例子中的subscribe方法 订阅状态变化 将 响应动作放入缓存列表
let subscribe = (listener) => {
listeners.push(listener);
}
dispath();
return {
getState,
dispath,
subscribe
}
}
上述即是观察者模式在redux中的应用。createStore的两个核心Api中的subscribe与dispatch分别对应了观察者模式中的订阅,发布方法。
Vue 中的体现
Vue中利用了观察者模式进行依赖收集。当监听到数据变化时来统一通知它所有依赖项进行批量更新。以下是简化提取的相关代码。
export default class Dep {
static target: ?Watcher;
id: number;
subs: Array<Watcher>;
constructor () {
this.id = uid++
// 缓存列表
this.subs = []
}
// 添加订阅 类比示例一代码中的 subscribe
addSub (sub: Watcher) {
this.subs.push(sub)
}
removeSub (sub: Watcher) {
remove(this.subs, sub)
}
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
// 发布 类比示例一代码中的publish
notify () {
// stabilize the subscriber list first
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
以上即是个人对观察者模式的一些理解和总结。偏差之处,欢迎指正。