描述对象间一对多的关系,当一个状态改变时,所有依赖于它的对象都能够得到通知并更新。
从概念上来说,发布-订阅者模式只是观察者模式的一种优化方案,他们都是描述对象间一对多的关系,如果有一个状态改变,则所有依赖于它的对象都会得到更新。
见的最多的就是vue中的实现原理。
先回顾一下观察者模式中创建副本的例子。
// 观察者模式
// 目标(被观察者)
class Team {
constructor(fbName, player) {
// 定义一个列表来表示一个队伍
this.list = [];
// 自己需要占一个位置
this.list.push(player);
}
// 开启副本
start () {
console.log('队伍人数达标,可以开启副本了')
// 通知队员,副本开启
this.list.map(ele => ele.start())
}
// 增加队员
add (player) {
this.list.push(player);
// 人数达标,通知队员准备
if (this.list.length === 5) {
this.start();
}
}
// 踢出队员
remove (player) {
console.log(`${player.name}实力不够,踢出队伍`)
// 找到这个人在队伍中的位置
let index = this.list.indexOf(player)
// 踢出队伍
this.list.splice(index, 1);
}
}
// 观察者(玩家)
class Player {
constructor(name) {
// 定义一个玩家
this.name = name;
}
add (army) {
// 加入某个队伍
army.add(this)
}
// 确认开启副本功能
start () {
console.log(`${this.name}已经点击了确认,随时可以开启副本`)
}
}
// 创建玩家
let player1 = new Player('赵一')
let player2 = new Player('钱二')
let player3 = new Player('孙三')
let player4 = new Player('李四')
let player5 = new Player('周五')
let player6 = new Player('吴六')
// 明确副本名称,以及确定队长
let team = new Team('大闹天宫副本', player1);
// 添加队员
player2.add(team)
player3.add(team)
team.remove(player2)
player4.add(team)
player5.add(team)
player6.add(team)
这里可以看到,观察者模式的任务中心其实是在目标(被观察者)内部,观察者和目标(被观察者)中间虽然说实现了松耦合,但是还是存在一定的直接关联,而发布-订阅者模式的任务中心是放在调度中心,观察者(订阅者)和被观察者(发布者)并没有直接的关联,或者说这两者没有耦合关系。
发布-订阅者模式中的耦合只存在于发布者-调度中心、订阅者-调度中心之间。
新增任务大厅
将上边的游戏副本模式修改一下,副本组队不再需要队长组队了,队长在只需任务大厅启动一个副本团队任务,然后就可以做其他事情了,其他玩家在任务大厅中响应任务大厅中的团队任务,队长确认并人数达标后就可以开启副本了。
// 发布-订阅者模式
// 调度中心
class TaskCenter {
// 创建任务大厅
constructor () {
// 创建任务面板
this.list = {};
}
create (name, team) {
// 创建任务面板
this.list[name] = {};
this.list[name].team = team;
}
// 创建副本
add (name, player) {
// 创建者即为团队的第一个人
this.list[name].team.add(player);
}
start (name) {
this.list[name].team.list.forEach(ele => ele.start())
}
}
// 实例化调度中心
var taskCenter = new TaskCenter();
创建玩家
// 订阅者(玩家)
class Player {
constructor(name) {
// 定义一个玩家
this.name = name;
}
add (name) {
// 加入某个队伍
taskCenter.add(name, this)
}
// 确认开启副本功能
start () {
console.log(`${this.name}已经点击了确认,随时可以开启副本`)
}
}
// 实例化玩家
let player1 = new Player('赵一')
let player2 = new Player('钱二')
let player3 = new Player('孙三')
let player4 = new Player('李四')
let player5 = new Player('周五')
let player6 = new Player('吴六')
创建团队
// 发布者(团队)
class Team {
constructor (creater, name) {
this.list = [];
this.name = name;
this.add(creater)
}
// 添加成员
add (player) {
this.list.push(player);
// 当团队玩家数量达标,则团队通知成员准备进入副本
if (this.list.length === 5) {
taskCenter.start(this.name);
}
}
}
开始游戏
// 创建副本
let name = Symbol('大闹天宫');
// 实例化团队
let team = new Team(player1, name);
taskCenter.create(name, team);
// 队员加入副本
taskCenter.add(name, player2);
taskCenter.add(name, player3);
player5.add(name);
taskCenter.add(name, player4);
解析
从上面的代码可以看到,团队(发布者)和玩家(订阅者)之间并没有直接关联,而是通过任务大厅的任务面板(调度中心)将两者联系起来的,包括新增队员和开启副本都是通过taskCenter.add()和taskCenter.start()触发的,这样的话发布者和订阅者中间并没有直接关联,这样就做到了两者之间的零耦合。
对比观察者模式,发布-订阅者模式和观察者模式的最大区别在于:
- 观察者模式的任务中心在目标(被观察者)内部
- 发布-订阅者模式的任务中心是放在调度中心
- 观察者模式中,观察者和目标之间实现了松耦合
- 发布-订阅者模式中,订阅者和发布者之间实现了零耦合
更多相关文档,请见:
线上地址 【前端橘子君】
GitHub仓库【前端橘子君】