在我的理解中,观察者模式,又叫发布/订阅模式,是一种是一种很便捷的用于不同模块之间相互通信的一种方式,类似于事件又不同于事件。

在我目前的理解中,观察者模式应该有三个部分组成,发布者,订阅者,管理平台,发布者发布信息到管理平台,管理平台收集到信息之后分发给订阅者,用更浅白的语言来形容我对观察者模式的理解就是函数调用,在不同模块之间通过函数调用进行通信。

言语之间不太好解释,还是来看看代码,可以更快的理解我所说的意思,先看管理平台是如何工作的:

letevent=[];classTest{staticaddEvent(type,fn){if(!event[type]){event[type]=[fn];}else{event[type].push(fn);}}staticdispath(type){lets=[],i=1;while(arguments[i]){s.push(arguments[i]);i++;}if(!event[type]){console.error('noEvent!');}else{for(leti=0;i<event[type].length;i++){event[type][i](...s);}}}}

我这里起名字的时候是用了Event的那一套名字,但和Event那一套其实没有什么关系,完全可以换成不同的名字。

看上面的代码:

letevent=[];

这是一个数组,用来存放订阅者的信息,也就是订阅的事件类型,和触发函数,最终所形成的样式是这样的:

['type1':[fn1,fn2,fn3],'type2':[fn4],'type3':[fn5,fn6]]

实际上就是有三个订阅者订阅type1,触发事件分别为fn1,fn2,fn3,后面等同。当发布者发布type1的时候,就会调用函数fn1,fn2,等等。

因为订阅者须要提前订阅才能接收到被订阅的内容,所以,我们先看订阅者是如何订阅的:

staticaddEvent(type,fn){if(!event[type]){event[type]=[fn];}else{event[type].push(fn);}}

在node.js中我们声明了一个类名为Test,里面有个静态方法 addEvent,两个参数,第一个是订阅的事件类型,第二个是触发函数,当还没有人订阅该事件的时候,就先创建该事件的函数空间,实际上就是一个数组,把事件名当索引,把触发事件存到该索引的元素下,如果已经有人订阅了该事件,就直接把触发事件push到对应索引的元素下。这样就可以记录下所有订阅者订阅的事件了。


然后是发布者发布事件:

staticdispath(type){lets=[],i=1;while(arguments[i]){s.push(arguments[i]);i++;}if(!event[type]){console.error('noEvent!');}else{for(leti=0;i<event[type].length;i++){event[type][i](...s);}}}

这也是一个静态函数,是用来把发布者发布的消息分发到每一个订阅者,while循环中是对参数的处理,把除第一个参数之外的所有参数集中到一个数组里,方面后面展开,这样可以还原到发布消息时的参数顺序,方面订阅者更好的接收参数,然后就是在所有的订阅者中找到第一个参数(也就是发布者发布的事件)所对应的所有触发函数,然后循环调用一次并把除事件之外的参数传过去。


这样的话,管理平台就组建好了。下面就是订阅,因为不先订阅是不可能收到发布者发布的消息的。

Test.addEvent('A',function(name,age){console.log('hello,thisis"A"Event1,hasarg:','name:',name,'age:',age);});Test.addEvent('B',function(num){console.log('hello,thisis"B"Event2,hasarg:',num);});Test.addEvent('C',function(obj){console.log('hello,thisis"C"Event3,hasarg:',obj);});Test.addEvent('D',function(arr){console.log('hello,thisis"D"Event4,hasarg:',arr);});Test.addEvent('E',function(date){console.log('hello,thisis"E"Event5,hasarg:',date);});

上面我们订阅了五个事件,每个事件所要接收的参数类型都不一样。并且订阅类型A须要接收两个参数,

订阅是不是很简单?好吧,其实发布者更简单:

Test.dispath('A','xiaoming',17);Test.dispath('B',123);Test.dispath('C',{say:'helloword'});Test.dispath('D',[1,2,3]);Test.dispath('E',newDate());

发布事件ABCDE,后面跟上发布的内容,然后订阅者就可以接发布时的顺序来接收参数。在项目中的不同模块之间进行通信就变得非常简单了。

到了这里,我想大家应该也都发现了我所说的订阅/发布 实际上就是函数调用是怎么回事了,订阅者把将要因事件而被触发的函数通过函数调用传到管理平台上,发布者把须要发布的事件也通过函数调用传到管理平台上,然后管理平台则根据不同的事件调用不同的函数。

代码不够精练,格式不够标准......

好吧,完了。

------------------------------------------------------------------------------------

另外,记录一点和该话题没多大关系但用到的一点内容,就是参数传递那一块,因为管理平台不能确定发布者会传多少参数进来,所以用到了Node.js的一个特性:arguments,在node.js中不论函数有有没有声明接收的参数,每个函数中都会有一个局部变量arguments,它是一个object对象,会把外界调用该函数时所传进来的参数整合进来,然后把传参的顺序做为属性,比如上面的A事件,在管理平台的dispath函数中arguments就是这个样子的:

{'0':'A',‘1’:‘xiaoming’,'2':17}

对象直接用 ... 这个符号拆分的话会报错,所以我把arguments用while重新组合成了数组,然后用 ...符号来拆分数组再进行参数传递。