用JS写一个发布订阅模式

什么是发布订阅模式?能手写实现一下吗?它和观察者模式有区别吗?...

1 场景引入

我们先来看这么一个场景:

假设现在有一个社交平台,平台上有一个大V叫Nami

Nami很牛,多才多艺,目前她有2个技能:会写歌、会拍视频

她会把这些作品发布到平台上。关注她的粉丝就会接收到这些内容

现在他已经有3个粉丝了,分别是:Luffy、Zoro、Sanji

每次只要Nami一发布作品,3个粉丝的账号上收到的消息就会更新

现在用代码来表示:

const luffy = { update: function (songs, videos) { console.log(songs, videos); }, }; const zoro = { update: function (songs, videos) { console.log(songs, videos); }, }; const sanji = { update: function (songs, videos) { console.log(songs, videos); }, }; const nami = { // 只要Nami的作品一更新,这个方法就会被调用 workUpdate: function () { // 获取作品 const songs = this.getSongs(); const videos = this.getVideos(); // 账号更新 luffy.update(songs, videos); zoro.update(songs, videos); sanji.update(songs, videos); }, getSongs: function () { return "mp3"; }, getVideos: function () { return "mp4"; }, };

现在问题来了

如果Nami又收获了一个粉丝Robin,我既要添加一个robin对象,又要修改workUpdate方法

如果Nami又有了一项新技能:写小说,我既要修改workUpdate函数,又要修改每个粉丝对象中的update方法,因为参数增加了一个

发现问题没有?

粉丝对象和大V对象之间的耦合度太高,导致两者很难各自扩展

2 代码优化 2.1 解决增加粉丝问题

先来解决上述第1个问题,使得增加粉丝的时候不用再修改workUpdate方法

首先,我们将“大V”抽象成一个类Star,用数组fans来保存粉丝列表,并新增一个添加粉丝的方法addFans

class Star { constructor() { this.fans = []; } addFans(fan) { this.fans.push(fan) } workUpdate() { const songs = this.getSongs(); const videos = this.getVideos(); this.fans.forEach((item) => item.update(songs, videos)); } getSongs() { return "MP3"; } getVideos() { return "MP4"; } }

接着,将“粉丝”也抽象成一个类Fan,我们在创建粉丝对象的时候传入“大V”对象,调用该大V的addFans方法来添加到粉丝列表

class Fan { constructor(name, star) { this.name = name this.star = star this.star.addFans(this) } update(songs, videos) { console.log(songs, videos); } }

现在我们添加粉丝就不必再更改代码了

const nami = new Star() const luffy = new Fan("luffy", nami); const zoro = new Fan("zoro", nami); const sanji = new Fan("sanji", nami); const robin = new Fan("robin", nami); nami.workUpdate()

2.2 解决添加作品问题

我们新增一个works数组来保存大V的作品,并且为其添加get和set方法

class Star { constructor() { this.fans = []; this.works = []; } addFans(fan) { this.fans.push(fan); } setWorks(work) { this.works.push(work); // 添加作品后,调用更新方法 this.workUpdate(); } getWorks() { return this.works; } workUpdate() { this.fans.forEach((item) => item.update()); } }

对类Fan进行相应修改:

class Fan { constructor(name, star) { this.name = name this.star = star this.star.addFans(this) } update() { console.log(`${this.name}:${this.star.getWorks()}`) } }

现在大V添加作品就不必再更改代码了:

const nami = new Star(); nami.setWorks('song') nami.setWorks('video') nami.setWorks('novel') const luffy = new Fan("luffy", nami); const zoro = new Fan("zoro", nami); const sanji = new Fan("sanji", nami); nami.workUpdate();

3 观察者模式

可以看到,在上述例子中,一个nami对象和多个粉丝对象之间存在着一种一对多的依赖关系,当nami对象有作品更新的时候,所有关注她的粉丝对象都会收到通知。

事实上,这就是观察者模式

观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新

我们将2.2中的代码进行进一步的抽象:

将“粉丝”看作观察者(Observer),将“大V”看作被观察的对象,称为主题(Subject)

Subject维护一个观察者列表observerList(原fans数组)。当Subject的状态发生变化(原作品更新)时,通过调用notify(原workUpdate)方法通知所有观察者,执行它们的update方法

具体代码如下:

// 被观察者:主题 class Subject { constructor() { this.observerList = []; // 代表主题状态 this.state = 0; } addObserver(observer) { this.observerList.push(observer); } // 更改主题状态 setState(state) { this.state = state; // 状态改变后,通知所有观察者 this.notify(); } getState() { return this.state; } notify() { this.observerList.forEach((observer) => observer.update()); } } // 观察者 class Observer { constructor(name, subject) { this.name = name; this.subject = subject; this.subject.addObserver(this); } update() { console.log(`${this.name}:${this.subject.state}`); } }

4 经纪人登场

由于大V业务繁忙,所以他们需要经纪人来维持艺人与粉丝的联系

经纪人的工作包括:

维护大V的粉丝,经纪人手中会有一个粉丝名单

大V的新作品会交给经纪人,经纪人则负责把新作品发送给粉丝名单中的粉丝

抽象成一个类,如下:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwzwyy.html