在我的理解中,观察者模式,又叫发布/订阅模式,是一种是一种很便捷的用于不同模块之间相互通信的一种方式,类似于事件又不同于事件。
在我目前的理解中,观察者模式应该有三个部分组成,发布者,订阅者,管理平台,发布者发布信息到管理平台,管理平台收集到信息之后分发给订阅者,用更浅白的语言来形容我对观察者模式的理解就是函数调用,在不同模块之间通过函数调用进行通信。
言语之间不太好解释,还是来看看代码,可以更快的理解我所说的意思,先看管理平台是如何工作的:
let event = []; class Test{ static addEvent(type,fn){ if(!event[type]){ event[type] = [fn]; }else{ event[type].push(fn); } } static dispath(type){ let s = [] ,i=1; while (arguments[i]) { s.push(arguments[i]); i++; } if(!event[type]){ console.error(‘no Event!‘); }else{ for(let i = 0;i<event[type].length;i++){ event[type][i](...s); } } } }
我这里起名字的时候是用了Event的那一套名字,但和Event那一套其实没有什么关系,完全可以换成不同的名字。
看上面的代码:
let event = [];
这是一个数组,用来存放订阅者的信息,也就是订阅的事件类型,和触发函数,最终所形成的样式是这样的:
[ ‘type1‘ : [fn1,fn2,fn3], ‘type2‘ : [fn4], ‘type3‘ : [fn5,fn6] ]
实际上就是有三个订阅者订阅type1,触发事件分别为fn1,fn2,fn3,后面等同。当发布者发布type1的时候,就会调用函数fn1,fn2,等等。
因为订阅者须要提前订阅才能接收到被订阅的内容,所以,我们先看订阅者是如何订阅的:
static addEvent(type,fn){ if(!event[type]){ event[type] = [fn]; }else{ event[type].push(fn); } }
在node.js中我们声明了一个类名为Test,里面有个静态方法 addEvent,两个参数,第一个是订阅的事件类型,第二个是触发函数,当还没有人订阅该事件的时候,就先创建该事件的函数空间,实际上就是一个数组,把事件名当索引,把触发事件存到该索引的元素下,如果已经有人订阅了该事件,就直接把触发事件push到对应索引的元素下。这样就可以记录下所有订阅者订阅的事件了。
然后是发布者发布事件:
static dispath(type){ let s = [] ,i=1; while (arguments[i]) { s.push(arguments[i]); i++; } if(!event[type]){ console.error(‘no Event!‘); }else{ for(let i = 0;i<event[type].length;i++){ event[type][i](...s); } } }
这也是一个静态函数,是用来把发布者发布的消息分发到每一个订阅者,while循环中是对参数的处理,把除第一个参数之外的所有参数集中到一个数组里,方面后面展开,这样可以还原到发布消息时的参数顺序,方面订阅者更好的接收参数,然后就是在所有的订阅者中找到第一个参数(也就是发布者发布的事件)所对应的所有触发函数,然后循环调用一次并把除事件之外的参数传过去。
这样的话,管理平台就组建好了。下面就是订阅,因为不先订阅是不可能收到发布者发布的消息的。
Test.addEvent(‘A‘,function (name,age){ console.log(‘hello,this is "A" Event 1,has arg:‘,‘name:‘,name,‘age:‘,age); }); Test.addEvent(‘B‘,function (num){ console.log(‘hello,this is "B" Event 2,has arg:‘,num); }); Test.addEvent(‘C‘,function (obj){ console.log(‘hello,this is "C" Event 3,has arg:‘,obj); }); Test.addEvent(‘D‘,function (arr){ console.log(‘hello,this is "D" Event 4,has arg:‘,arr); }); Test.addEvent(‘E‘,function (date){ console.log(‘hello,this is "E" Event 5,has arg:‘,date); });
上面我们订阅了五个事件,每个事件所要接收的参数类型都不一样。并且订阅类型A须要接收两个参数,
订阅是不是很简单?好吧,其实发布者更简单:
Test.dispath(‘A‘,‘xiaoming‘,17); Test.dispath(‘B‘,123); Test.dispath(‘C‘,{say:‘hello word‘}); Test.dispath(‘D‘,[1,2,3]); Test.dispath(‘E‘,new Date());
发布事件ABCDE,后面跟上发布的内容,然后订阅者就可以接发布时的顺序来接收参数。在项目中的不同模块之间进行通信就变得非常简单了。
到了这里,我想大家应该也都发现了我所说的订阅/发布 实际上就是函数调用是怎么回事了,订阅者把将要因事件而被触发的函数通过函数调用传到管理平台上,发布者把须要发布的事件也通过函数调用传到管理平台上,然后管理平台则根据不同的事件调用不同的函数。
代码不够精练,格式不够标准......
好吧,完了。
------------------------------------------------------------------------------------
另外,记录一点和该话题没多大关系但用到的一点内容,就是参数传递那一块,因为管理平台不能确定发布者会传多少参数进来,所以用到了Node.js的一个特性:arguments,在node.js中不论函数有有没有声明接收的参数,每个函数中都会有一个局部变量arguments,它是一个object对象,会把外界调用该函数时所传进来的参数整合进来,然后把传参的顺序做为属性,比如上面的A事件,在管理平台的dispath函数中arguments就是这个样子的:
{ ‘0‘ : ‘A‘, ‘1’:‘xiaoming’, ‘2‘ : 17 }
对象直接用 ... 这个符号拆分的话会报错,所以我把arguments用while重新组合成了数组,然后用 ...符号来拆分数组再进行参数传递。
本文出自 “__无字书” 博客,请务必保留此出处http://wuzishu.blog.51cto.com/6826059/1757644
原文:http://wuzishu.blog.51cto.com/6826059/1757644