上述代码我们用职责链模式组织了多个校验逻辑,这几个校验之间相互之间没有依赖,如果以后需要减少某个校验,只需要将它从validators数组中删除即可,如果要添加就往这个数组添加就行了。这几个校验器之间的耦合度就大大降低了,而且他们封装的是promise,完全还可以用到其他模块去,其他模块根据需要组织自己的职责链就行了。
观察者模式观察者模式还有个名字叫发布订阅模式,这在JS的世界里可是大名鼎鼎,大家或多或少都用到过,最常见的就是事件绑定了,有些面试还会要求面试者手写一个事件中心,其实就是一个观察者模式。观察者模式的优点是可以让事件的产生者和消费者相互不知道,只需要产生和消费相应的事件就行,特别适合事件的生产者和消费者不方便直接调用的情况,比如异步中。我们来手写一个观察者模式看看:
class PubSub { constructor() { // 一个对象存放所有的消息订阅 // 每个消息对应一个数组,数组结构如下 // { // "event1": [cb1, cb2] // } this.events = {} } subscribe(event, callback) { if(this.events[event]) { // 如果有人订阅过了,这个键已经存在,就往里面加就好了 this.events[event].push(callback); } else { // 没人订阅过,就建一个数组,回调放进去 this.events[event] = [callback] } } publish(event, ...args) { // 取出所有订阅者的回调执行 const subscribedEvents = this.events[event]; if(subscribedEvents && subscribedEvents.length) { subscribedEvents.forEach(callback => { callback.call(this, ...args); }); } } unsubscribe(event, callback) { // 删除某个订阅,保留其他订阅 const subscribedEvents = this.events[event]; if(subscribedEvents && subscribedEvents.length) { this.events[event] = this.events[event].filter(cb => cb !== callback) } } } // 使用的时候 const pubSub = new PubSub(); pubSub.subscribe('event1', () => {}); // 注册事件 pubSub.publish('event1'); // 发布事件 实例:Node.js的EventEmitter观察者模式的一个典型应用就是Node.js的EventEmitter,我有另一篇文章从发布订阅模式入手读懂Node.js的EventEmitter源码从异步应用的角度详细讲解了观察者模式的原理和Node.js的EventEmitter源码,我这里就不重复书写了,上面的手写代码也是来自这篇文章。
实例:转圈抽奖一样的,看了优秀框架的源码,我们自己也要试着来用一下,这里的例子是转圈抽奖。想必很多朋友都在网上抽过奖,一个转盘,里面各种奖品,点一下抽奖,然后指针开始旋转,最后会停留到一个奖品那里。我们这个例子就是要实现这样一个Demo,但是还有一个要求是每转一圈速度就加快一点。我们来分析下这个需求:
要转盘抽奖,我们肯定先要把转盘画出来。
抽奖肯定会有个结果,有奖还是没奖,具体是什么奖品,一般这个结果都是API返回的,很多实现方案是点击抽奖就发起API请求拿到结果了,转圈动画只是个效果而已。
我们写一点代码让转盘动起来,需要一个运动效果
每转一圈我们需要加快速度,所以还需要控制运动的速度
通过上面的分析我们发现一个问题,转盘运动是需要一些时间的,当他运动完了需要告诉控制转盘的模块加快速度进行下一圈的运动,所以运动模块和控制模块需要一个异步通信,这种异步通信就需要我们的观察者模式来解决了。最终效果如下,由于只是个DEMO,我就用几个DIV块来代替转盘了: