代码写了好几年了,设计模式一直处于忘我的状态。大多数关于设计模式的文章都使用基于类的静态类型语言,例如Java和C++。作为前端开发人员,js是一门基于原型的动态语言。职能已成为一等公民。模式略有不同,甚至简单得不像使用设计模式,有时会引起一些混淆。下面按照“场景”-“设计模式定义”-“代码实现”-“总则”的顺序进行总结。如有不妥之处,欢迎交流讨论。场景假设我们正在开发一个食品配送网站。进入网站,第一步是请求后台接口获取用户常用的外卖地址。然后再去请求其他接口,渲染页面。如果什么都不考虑,可以直接这样写://getAddress异步请求//页面上有A、B、C三个模块。在进行下一步之前,您需要获取地址。//A、B、C这三个模块都是不同的别人写的,它提供了不同的方法让我们调用getAddress().then(res=>{constaddress=res.address;A.update(address)B.next(address)C.change(address)})this当页面上多了一个模块D时,我们还需要获取地址才能进行下一步。我们要通过请求地址的代码,加上D模块的调用。//getAddress异步请求//页面上有A、B、C三个模块。在进行下一步之前,我们需要获取地址。//A、B、C三个模块是不同人写的,给我们提供了不同的方法CallgetAddress().then(res=>{constaddress=res.address;A.update(address)B.next(address)C.change(address)D.init(address)})看各个模块和地址获取模块耦合严重。如果A、B、C模块有变化或新增模块,需要深入地址获取代码进行修改。如果你不小心,你可能会纠正这个问题。这时候就需要观察者模式了。设计模式的定义,请看维基百科的介绍:观察者模式是一种软件设计模式,其中一个对象,名为主体,维护一个其依赖者列表,称为观察者,并自动通知他们任何状态变化,通常通过调用他们的方法之一。”一个很好理解的设计模式,有一个subject对象,然后有很多observers观察者对象,当subject对象发生变化时,只要通知observer对象即可。再看UML图和时序图:每个Observers实现update方法,调用Subject对象的attach方法订阅变化。当Subject发生变化时,调用Observer的update方法通知观察者。先用java写一个简单的例子:公众号文章可以看做是主题,会不定期更新。那么每个用户就是一个Observer,订阅公众号,消息一有更新就可以收到。importjava.util.ArrayList;interfaceObserver{publicvoidupdate();}//提取Subject抽象类Subject的公共部分{privateArrayListlist=newArrayList();publicvoidattach(Observerobserver){list.add(observer);}publicvoiddetach(观察者观察者){list.remove(观察者);}publicvoidnotifyObserver(){for(Observerobserver:list){observer.update();}}}//具体公众号,提供写文章和获取文章classWindLiangextendsSubject{privateStringpost;publicvoidwritePost(Stringp){post=p;}publicStringgetPost(){返回帖子;}}//小明类XiaoMingimplementsObserver{privateWindLiangsubject;小明(WindLiangsub){subject=sub;}@Overridepublicvoidupdate(){Stringpost=subject.getPost();System.out.println("我收到了"+post+"并且喜欢");}}//小杨类XiaoYangimplementsObserver{私有WindLiang主题;小杨(WindLiangsub){subject=sub;}@Overridepublicvoidupdate(){Stringpost=subject.getPost();System.out.println("我收到"+post+"并转发");}}//小刚类XiaogangimplementsObserver{privateWindLiangsubject;小刚(WindLiangsub){subject=sub;}@Overridepublicvoidupdate(){Stringpost=subject.getPost();系统输出。println("我收到"+post+"并保存");}}publicclassMain{publicstaticvoidmain(String[]args){WindLiangwindliang=newWindLiang();//SubjectXiaoMingxiaoMing=newXiaoMing(windliang);小阳xiaoYang=new小阳(windliang);小刚xiaoGang=new小刚(windliang);//添加观察者windliang.attach(xiaoMing);windliang.attach(xiaoYang);windliang.attach(xiaoGang);windliang.writePost("新文章-观察者模式,balabala");//更新文章windliang.notifyObserver();//通知观察者}}输出结果如下:上面的实现主要是为了遵守最原始的定义,调用update时没有传递任何参数。如果观察者需要的参数一致,其实这里也可以直接传递更新数据,这样观察者就不需要像上面那样手动调用subject.getPost()获取更新数据了。这两种不同方法中的前者称为拉取(pull)模式,即在收到Subject的通知后,通过内部的Subject对象调用相应的方法来获取需要的数据。后者称为推送模式。当Subject更新时,数据被推送给观察者,观察者可以直接使用。下面用js改写为推送方式:constWindLiang=()=>{constlist=[];letpost="尚未更新";返回{附加(更新){list.push(更新);},分离(更新){让findIndex=-1;for(leti=0;i{constaddress=res.address;observers.forEach(update=>update(address))})通过观察者模式,我们将获取地址后的操作解耦。以后新模块只需要注册观察者即可。当getAddress很复杂时,观察者模式会让以后的改动一目了然,不会影响getAddress的逻辑。如有必要,还可以将观察者作为新模块提取到新文件中,以防止文件变得过于臃肿。TotalObserver模式更容易理解。通过抽象一个Subject和多个观察者,缓解它们之间的过度耦合。简单的说,就是在异步完成后,使用回调函数调用传入的回调。但是上面写的观察者模式还是有一些不足:Subject还是需要自己维护一个观察者列表来进行推送和更新。如果其他模块也需要使用观察者模式,模块本身需要维护一个新的观察者列表,之前的代码不能复用。Subject需要知道观察者提供了哪些方法以便将来回调。下一篇文章会继续完善上面的写法,观察者模式的本质思想不变(一个对象发生变化,然后通知其他观察者对象更新)。但是在写法上,会引入一个中间平台,方便代码更好的复用,让Subject和observers更彻底的解耦,并给它起了个新名字“发布-订阅模式”。