当前位置: 首页 > Web前端 > HTML

面试官:你会用JS写一个发布-订阅模型吗?

时间:2023-03-28 10:29:44 HTML

什么是发布-订阅模式?你能用手做吗?它与观察者模式有什么不同?...1场景介绍先来看这样一个场景:假设现在有一个社交平台,平台上有个大V叫NamiNami,非常有才华,多才多艺。目前,她有两项技能:会写歌和拍视频。在平台上发布这些作品。关注她的粉丝将收到这些内容。现在他有3个粉丝,分别是:路飞、索隆和山治。娜美每发布一部作品,三位粉丝账号上收到的消息都会更新。现在用代码表示:constluffy={update:function(songs,videos){console.log(songs,videos);},};constzoro={update:function(songs,videos){console.log(songs,videos));},};constsanji={update:function(songs,videos){console.log(songs,videos);},};constnami={//每当Nami的作品更新时,这个方法就会调用workUpdate:function(){//获取作品constsongs=this.getSongs();const视频=this.getVideos();//账户更新luffy.update(songs,videos);zoro.update(歌曲,视频);sanji.update(歌曲,视频);},getSongs:function(){返回"mp3";},getVideos:function(){返回"mp4";},};现在问题来了,如果娜美再得到一个粉丝罗宾,我需要添加一个罗宾对象并修改workUpdate方法。如果娜美有新技能:写小说,我得修改每个粉丝对象中的workUpdate函数和update方法,因为参数Addedafoundproblem?扇形对象与大V对象的耦合度太高,导致两者难以单独扩展。2代码优化2.1解决加粉丝问题。我们先解决上面的第一个问题,这样在添加粉丝的时候就不需要去修改workUpdate方法了。首先,我们将“大V”抽象成一个类Star,使用数组fans保存粉丝列表,新增一个添加粉丝的方法addFansclassStar{constructor(){this.fans=[];}addFans(fan){this.fans.push(fan)}workUpdate(){constsongs=this.getSongs();constvideos=this.getVideos();this.fans.forEach((item)=>item.update(songs,videos));}getSongs(){返回“MP3”;}getVideos(){返回“MP4”;}}接下来,“粉丝”也被抽象成类Fan。我们在创建粉丝对象时,传入“大V”对象,调用大V的addFans方法将其添加到粉丝列表中。类范{构造函数(名称,明星){这个。name=namethis.star=starthis.star.addFans(this)}update(songs,videos){console.log(songs,videos);}}现在我们不用修改代码就可以添加粉丝了("sanji",nami);constrobin=newFan("robin",nami);nami.workUpdate()2.2解决添加作品的问题我们新增一个作品数组来保存大V的作品,并为其添加get和set方法classStar{constructor(){this.fans=[];这个.works=[];}addFans(粉丝){this.fans.push(粉丝);}setWorks(工作){this.works.p嘘(工作);//添加作品后,调用更新方法this.workUpdate();}getWorks(){返回this.works;}workUpdate(){this.fans.forEach((item)=>item.update());}}相应地修改类Fan:classFan{constructor(name,star){this.name=namethis.star=starthis.star.addFans(this)}update(){console.log(`${this.name}:${this.star.getWorks()}`)}}既然大V加了作品,就不用改代码了:constnami=newStar();nami.setWorks('song')nami.setWorks('视频')nami.setWorks('小说')constluffy=newFan("路飞",nami);constzoro=newFan("zoro",nami);constsanji=newFan("sanji",nami);nami.workUpdate();3观察者模式可以看到,在上面的例子中,一个nami对象和多个fan对象之间存在一对多的依赖关系,当nami对象有工作更新其实这就是Observer模式Observer模式:对象之间定义一对多的依赖关系,当一个对象的状态发生变化时,所有依赖其的对象都会得到通知并自动更新。我们在2.2中进一步抽象代码:将“粉丝”视为观察者(Observer),将“大V”视为观察对象,称为主题(Subject),主题维护一个观察者列表observerList(原粉丝数组)。当Subject状态发生变化(原work更新)时,通过调用notify(原workUpdate)方法通知所有观察者,执行其update方法的具体代码如下://Observed:subjectclassSubject{constructor(){这个.observerList=[];//表示主题状态this.state=0;}addObserver(观察者){this.observerList.push(观察者);}//改变主题状态setState(state){this.state=state;//当状态改变时,通知所有观察者this.notify();}getState(){返回这个状态;}notify(){this.observerList.forEach((observer)=>observer.update());}}//观察者类Observer{constructor(name,subject){this.name=name;this.subject=主题;this.subject.addObserver(this);}update(){console.log(`${this.name}:${this.subject.state}`);}}4代理登场。由于大V生意繁忙,他们需要经纪人来维系艺人与粉丝之间的联系。经纪人的工作包括:维护大V的粉丝,经纪手里会有一份粉丝名单。大V的新作品会交给代理,由代理负责将新作品发送给粉丝列表中的粉丝,并抽象成一个类,如下:classManager{constructor(){这个.fans=[];这个.works=[];}addFans(粉丝){this.fans.push(粉丝);}setWorks(工作){this.works.push(工作);//添加一个作品后,调用update方法这个.workUpdate();}getWorks(){返回this.works;}workUpdate(){this.fans.forEach((item)=>item.update());}}嗯?这段代码好像在哪里见过?没错,它和2.2中的Star类一模一样,只是改了类名。这样做有意义吗?其实代码是一模一样的,因为在2.2的Star类中,我们只写了发布(即发布作品)和订阅(即维护粉丝列表)相关的功能;而Star类本身可能不仅仅做这个工作,比如创作内容。现在我们把Star类中发布订阅的工作抽离出来,交给Manager全权负责。对于Star类,只需要在创建完成后将工作交给Manager即可。另一方面,粉丝不再直接与明星互动。订阅(该行为相当于将粉丝添加到Manager维护的粉丝列表)Manager,从Manager获取想要的作品,所以Star和Fan的代码如下:classStar{constructor(){}//createcreate(manager){//将创建的新工作交给代理manager.setWorks("newwork");}}classFan{constructor(name,manager){this.name=name;this.manager=经理;这个。经理。添加粉丝(这个);}update(){console.log(`${this.name}:${this.manager.getWorks()}`);}}5发布-订阅模式我们使用一个broker负责发布Work和subscription,不需要Star和Fan直接交互,达到两者解耦的效果。这就是发布-订阅模型。我们在4中进一步抽象Manager:把“粉丝”看成订阅者(Subscriber);把“大V”看成是内容的发布者,在发布-订阅模型中称为发布者;将“经纪人”视为发布-订阅中心(或中间人Broker)。具体代码如下://发布订阅调度中心类Broker{constructor(){this.subscribers=[];//表示主题状态this.state=0;}//订阅subscribe(subscriber){this.subscribers.push(subscriber);}//改变话题状态setState(state){this.state=state;//状态改变后,发布this.publish();}getState(){返回状态;}//发布publish(){this.subscribers.forEach((subscriber)=>subscriber.update());}}//发布者类Publisher{constructor(){}changeState(broker,state){broker.setState(state);}}classSubscriber{constructor(name,broker){this.name=name;this.broker=经纪人;this.broker.subscribe(this);}update(){console.log(`${this.name}:${this.broker.getState()}`);}}运行一下看看效果://创建一个调度中心constbroker=newBroker()//创建一个发布者constpublisher=newPublisher()//创建一个订阅者constsubscribe1=newSubscriber('s1',broker)constsubscribe2=newSubscriber('s2',broker)constsubscribe3=newSubscriber('s3',broker)//改变发布者状态并通知调度中心,调度中心会通知每个订阅者publisher.changeState(broker,1)6观察者模式和发布订阅模式的比较从角色的数量来看,观察者模式只有两个角色:观察者和被观察者发布订阅模式有三个角色:发布者,订阅者,和中介(发布-订阅中心)。从耦合度来看,观察者模型处于松耦合状态,即两者仍然有交互,但在没有交互的情况下很容易单独扩展。影响发布-订阅模型的发布者和订阅者之间完全没有耦合,达到了对象之间解耦的效果。从意图上看,两者:实现对象之间一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并自动更新公众号【前端】