观察者模式(Observer Pattern)
观察者模式是一种对象间的一对多依赖关系,当一个对象的状态发生变化时,所有依赖他的对象都将得到通知。通常运用在对象之间的消息通讯中。
// 比如现在有一群学生,可以组成小组,可以寻求帮助
class Students {
constructor(name, level) {
this.name = name;
this.level = level;
this.tasks = [];
}
askForHelp(subject) {
console.log(`关于${subject}, ${this.level}${this.name}向大家寻求帮助!`)
this.tasks.forEach((fn) => {
fn(subject);
});
}
makeTeam(student, fn) {
console.log(`${this.level}${this.name}与${student.level}${student.name}组成了学习小组!`);
student.tasks.push(fn);
}
}
const studentWango = new Students('Wango', '学神');
const studentLily = new Students('Lily', '学霸');
const studentTom = new Students('Tom', '平平无奇的');
const studentPeter = new Students('Peter', '学渣');
// 老师说Peter的成绩比较差,所以大家都和Peter组成了学习小组
studentWango.makeTeam(studentPeter, function(subject) {
console.log(`Wango说:${subject}确实很难,我也花了好几分钟复习才堪堪得到了满分!`);
})
// 学神Wango与学渣Peter组成了学习小组!
studentLily.makeTeam(studentPeter, function(subject) {
console.log(`Lily说:${subject}!? 背就完事了!`);
})
// 学霸Lily与学渣Peter组成了学习小组!
studentTom.makeTeam(studentPeter, function(subject) {
console.log(`Tom说:${subject}的话,一定要好好学,但学不好也没事,我们都是凡人!`);
})
// 平平无奇的Tom与学渣Peter组成了学习小组!
// 有一天...
studentPeter.askForHelp('英语');
// 关于英语, 学渣Peter向大家寻求帮助!
// Wango说:英语确实很难,我也花了好几分钟复习才堪堪得到了满分!
// Lily说:英语!? 背就完事了!
// Tom说:英语的话,一定要好好学,但学不好也没事,我们都是凡人!
发布订阅模式(Pub-Sub Pattern)
发布订阅模并不在24
种设计模式之中,只是观察者模式的一种变体。相比观察者模式,订阅发布模式新增了一个任务调度中心,订阅者将自己想要订阅的事件注册到调度中心,当发布者发布该事件到调度中心,也就是触发该事件时,由调度中心同一调度订阅者注册到调度中心的代码。
// 同样的学习小组
class Students {
constructor(name, level) {
this.name = name;
this.level = level;
}
// 订阅事件
on(type, fn) {
ClassRoom.subscribe(type, fn);
}
// 发布内容
emit(type, content) {
ClassRoom.askForHelp(type, content);
}
}
// 任务调度中心
class ClassRoom {
static topic = Object.create(null)
// 对于某个事件发布任务
static askForHelp(type, content) {
console.log(`${type}:${content}`);
if (!ClassRoom.topic[type] || ClassRoom.topic[type].length === 0) {
console.log(`然而并没有学生对${type}感兴趣!`);
return;
}
ClassRoom.topic[type].forEach((fn) => {
fn(content);
});
}
// 订阅某个类型的事件
static subscribe(type, fn) {
if (!ClassRoom.topic[type]) {
ClassRoom.topic[type] = [];
}
console.log(`有学生订阅了${type}.`);
ClassRoom.topic[type].push(fn);
}
}
const studentWango = new Students('Wango', '学神');
const studentLily = new Students('Lily', '学霸');
const studentTom = new Students('Tom', '平平无奇的');
const studentPeter = new Students('Peter', '学渣');
studentWango.on('Life', function(type, content) {
console.log(`Wango说: 终于有人问生活上的问题了,果然还是做个凡人比较快乐啊!`);
})
// 有学生订阅了Life.
studentWango.on('English', function(type, content) {
console.log(`Wango说:英语确实很难,我也花了好几分钟复习才堪堪得到了满分!`);
})
// 有学生订阅了English.
studentTom.on('English', function(type, content) {
console.log(`Tom说:英语的话,一定要好好学,但学不好也没事,我们都是凡人!`);
})
// 有学生订阅了English.
studentLily.on('English', function(type, content) {
console.log(`Lily说: 英语?! 背就完事了!`);
})
// 有学生订阅了English.
studentLily.emit('Life', '这个世界有英雄吗?');
// Life:这个世界有英雄吗?
// Wango说: 终于有人问生活上的问题了,果然还是做个凡人比较快乐啊!
studentTom.emit('English', '单词记不住怎么办啊?');
// English:单词记不住怎么办啊?
// Wango说:英语确实很难,我也花了好几分钟复习才堪堪得到了满分!
// Tom说:英语的话,一定要好好学,但学不好也没事,我们都是凡人!
// Lily说: 英语?! 背就完事了!
studentPeter.emit('Game', '学什么?玩游戏啊!');
// Game:学什么?玩游戏啊!
// 然而并没有学生对Game感兴趣!
区别与联系
相比观察者模,订阅发布模式中的的订阅者和发布者分离更加彻底。订阅者在订阅的时候只关注事件本身,而不用关心是谁发布的信息;而发布者同样只需要关注发布的内容,不用关心是谁在订阅,如何处理。
总的来说,尽管订阅发布模式多了个调度中心,但使用起来可以更加灵活,对多事件的支持更好,当然,内存消耗更大,毕竟要为维护多个事件队列。