1、有一条古老的街道
有一条古老的街道,有一天,开了一个报社,是关于财经的。(定义一个发布者类Publish)
我们叫它“财经报社”。(实例化时定义报社名称publisherName)
它有一张订阅者登记表。(实例化时定义一个包含订阅者名称的登记表watcherLists)
用户登记表可以记录订阅者的名字。(添加订阅者名的方法addWatcher)
送报员把报纸送到每一个订阅者手里。(通知订阅者的方法notify)
订阅者不订阅时,报社可以移出订阅者的名字。(移出订阅者的方法removeWatcher)
报社万一哪天关门时,会清空订阅者列表。(清空订阅者的方法清空发布者列表)
上面的每一句话,都代表了一个伪代码,下面具体实现一个发布者类:
class Publish {
constructor(publisherName) {
// 发布者名称
this.publisherName = publisherName;
// 订阅者列表
this.watcherLists = []
}
// 添加订阅者
addWatcher(watcher) {
this.watcherLists.push(watcher)
}
// 通知订阅者
notify() {
const watcherLists = this.watcherLists.slice()
for (let i = 0, l = watcherLists.length; i < l; i++) {
watcherLists[i].update()
}
}
// 移除订阅者
removeWatcher(watcherName) {
if (!this.watcherLists.includes(watcherName)) {
return;
}
for (let i = 0; i < this.watcherLists.length; i++) {
if (this.watcherLists[i].watcherName === watcherName) {
this.watcherLists[i].removePublishers(this.publisherName)
this.watcherLists.splice(i, 1)
}
}
}
// 清空订阅者列表
clearWatchers() {
const watcherLists = this.watcherLists.slice()
for (let i = 0, l = watcherLists.length; i < l; i++) {
watcherLists[i].removePublishers(this.publisherName)
}
this.watcherLists = []
}
}
财经报馆开业,我们new个报馆实例。
const financialNewspaper = new Publish('财经报社')
2、来了两个财经爱好者
有一天来了两个财经爱好者。(定义一个订阅者类Watcher)
订阅者是有名称的。(实例化时定义报社名称watcherName)
订阅者订阅的可能不止一家报社。(实例化时定义一个包含报社(发布者)的笔记本publishers)
订阅者收到报纸后的行为。(实例时定义定义订阅者的行为(事件)fn)
订阅者是通过什么样的方式接收报纸的。(定义接收报纸(发布者发布的消息)的途径,这里统一为信箱方式update)
订阅者可以订阅其他报社的报纸。(添加发布者的方式addPublisher)
订阅者也可以取消某家报社的报纸。(移除发布者的方式removePublishers)
订阅者离开这条街道时,清空报社名称的笔记本。(清空发布者列表clearPublishers)
上面的每一句话,都代表了一个伪代码,下面具体实现一个订阅者类:
class Watcher {
constructor(watcherName, fn) {
this.watcherName = watcherName; // 订阅者名称
this.publishers = [] // 发布者列表
this.fn = fn // 监听者收到消息后的反应(事件)
}
// 更新自身事件(行为)
update() {
this.fn();
}
// 添加发布者
addPublisher(publisher) {
this.publishers.push(publisher)
}
// 移除发布者
removePublishers(publisherName) {
if (!this.publishers.includes(publisherName)) {
return;
}
for (let i = 0; i < this.publishers.length; i++) {
if (this.publishers[i].publisherName === publisherName) {
this.publishers[i].removeWatcher(this.watcherName) // 通知发布者删除订阅者
this.publishers.splice(index, 1) // 从发布者列表中清除发布者
}
}
}
// 清空发布者列表
clearPublishers() {
const publishers = this.publishers.slice()
for (let i = 0, l = publishers.length; i < l; i++) {
publishers[i].removeWatcher(this.watcherName)
}
this.publishers = []
}
}
关于订阅者,我们new两个订阅者实例。
const watcherA = new Watcher('watcherA', function () {
console.log('喝着茶,看着报纸')
})
// 定义订阅者B
const watcherB = new Watcher('watcherB', function () {
console.log('大清早,晨读报纸')
})
财经报刊添加了两个订阅者watcherA和watcherB。
financialNewspaper.addWatcher(watcherA)
financialNewspaper.addWatcher(watcherB)
// 可以打印发布者和发布者收集的订阅者列表
console.log(financialNewspaper, financialNewspaper.watcherLists);
两个细心的订阅者把财经报刊记录在了小本本上。
watcherA.addPublisher(financialNewspaper);
watcherB.addPublisher(financialNewspaper);
// 可以打印订阅者和订阅者订阅的报刊种类
console.log(watcherA, watcherA.publishers);
console.log(watcherB, watcherB.publishers);
3、订阅者收到报纸
第二天,送报员就把报纸投进了门口邮箱(相当于财经报刊进行了消息发布)
financialNewspaper.notify()
// watcherA和watcherB收到报纸(消息)后,就触发了他们的行为
// watcherA:'喝着茶,看着报纸'
// watcherB:'大清早,晨读报纸'
4、财经报社又来了个订阅者
有一天财经报社来了个watcherC,也订阅了报刊。
我们再new个订阅者watcherC:
const watcherC = new Watcher('watcherC', function () {
console.log('大晚上,熬夜看报纸')
})
报社把订阅者watcherC记录在了登记表上。
financialNewspaper.addWatcher(watcherC)
同样细心的订阅者watcherC也把财经报社记录在了小本本上。
watcherC.addPublisher(financialNewspaper);
5、街道上又开了家体育类报社
有一天街道上又开了个体育报社。
我们先new一个体育报社。
const sportsNewspaper = new Publish('体育报社')
watcherA和watcherC也是体育爱好者,所以订阅了体育报刊。
体育报社需要登记两个订阅者的姓名。
sportsNewspaper.addWatcher(watcherA)
sportsNewspaper.addWatcher(watcherC)
这两订阅者,又各自把体育报社记录在了小本本上。
watcherA.addPublisher(sportsNewspaper);
watcherC.addPublisher(sportsNewspaper);
6、有订阅者取消体育报刊的报纸
订阅者watcherC本来不喜欢运动,起初订阅体育报刊纯粹为了凑热闹,三天的劲头已过,他决定取消体育报刊的报纸。
watcherC.removePublishers('sportsNewspaper')
7、有订阅者要离开这条街道
有一天,watcherA要出国留学,所以就从小本本上划掉了记录的报刊名称,并且通知报社取消报纸的订阅,第二天,送报员就没再给watherA送报纸。
watcherA.clearPublishers()
这里watcherC清掉小本本上名称的同时,也会通知到报社,体育报社和财经报社同样会在等级表上清除watcherC的名称。
clearPublishers() {
const publishers = this.publishers.slice()
for (let i = 0, l = publishers.length; i < l; i++) {
publishers[i].removeWatcher(this.watcherName)
}
this.publishers = []
}
8、有报社要关门
岁月如梭,多年过去啦。
随着移动互联网的兴起,纸媒受到影响,这条街道的财经报社决定关门。
financialNewspaper.clearWatchers()
第二天就不再给登记表上的订阅者送报啦,订阅者收到消息后,从小本本上划掉了财经类报刊的名字。
clearWatchers() {
const watcherLists = this.watcherLists.slice()
for (let i = 0, l = watcherLists.length; i < l; i++) {
watcherLists[i].removePublishers(this.publisherName)
}
this.watcherLists = []
}
这里描述了发布者的产生、订阅者的产生、发布者发布消息的方式、订阅者接受消息的途径、订阅者接收到消息的行为、发布者的新增、订阅者的新增、发布者的离开和订阅者的离开等关系和逻辑。代码具体的执行结果,还需要学友自行运行验证。
总结
发布订阅者模式又叫观察者模式,它定义了对象间的一种一对多的关系。这种关系,既指一个发布者可以对应多个订阅者,又可以指一个订阅者也订阅多个发布者的消息。
写在最后
如有错误烦请贵手留言,如果帮助请点赞支持❤。