设计模式之发布-订阅模式
发布-订阅模式
发布-订阅模式与观察者模式是一个相似的模式,功能上是差不多的。
但观察者模式更合适面向对象的写法,所以在js里看情况使用,如果
不太习惯使用js的面向对象,那么发布-订阅模式就够了。
发布-订阅模式在现今使用的很多,在各大框架,插件都被使用,因为的
它的解耦合,可以大大的提高代码的复用性,比如现在的MVVM框架都有
使用它,或者模块化通信都有使用,如果拜读过jQuery源码的都应该看到
这个模式运用,只是或者你不太甚了解它,为什么它被运用的那么频繁了?
因为代码之间的高度解耦合,现在的都是追求代码复用性高,高质量代码的年代,
所以这个模式很适合,但它也有很大的bug,就是代码追踪可能给你增加难度,
这是很正常的,代码之间的关联关系已经不强,那么他们之间追踪起来就会难。
它运用例子场合,如房东与租客,现在的房东大多数都是通过中介把消息推送出去给租客,
所以租客只要和中介有关联就可以知道哪里有房子租。因为中介会租客推送房子的消息。
又或者如微信的关注了公众号,当你微信关闭时是没有收到公众号发布的消息的,但当你上线后
之前离线的发布的消息就会全部推送给你,因为你关注的它,这是个订阅的过程,和发布的过程。
所以可以先订阅再发布,也可以先发布再订阅。
/*发布-订阅模式*/ var Event=(function(){ var event, //默认命名空间名 _default="default"; event=function(){ //内部的方法 var _create,_listen,_trigger,_remove; //数组的操作 var _shift=Array.prototype.shift, _unshift=Array.prototype.unshift; //缓存空间 var cacheSpacename={}; //循环调用 var each=function(ary,fn){ //判断是否有方法被调用,使用来判断是否离线 var ret=false; for(var i=0,l=ary.length;i<l;i++){ var n=ary[i]; //调用方法,改变this ret=fn.call(n,i,n); } return ret; } _listen=function(key,fn,cache){ if(!cache[key]){ cache[key]=[]; } cache[key].push(fn); } _trigger=function(){ var cache=_shift.call(arguments); var key=_shift.call(arguments); var args=arguments; var _self=this; if(!cache[key]){ return false; } var ret=each(cache[key],function(){ //因为this改变,所以这里的this缓存里的函数 this.apply(_self,args); }); return ret; } _remove=function(key,fn,cache){ if(!cache[key]||!fn){ cache[key]=[]; return false; } for(var i=0;i<cache[key].length;i++){ if(cache[key][i]==fn){ cache[key].splice(i,1); i--; } } return true; } _create=function(namespace){ var cache,ret,offlineCache; if(!namespace){ namespace=_default; } //缓存空间,重点,使用来存缓存数据和离线数据 cache=cacheSpacename[namespace]?cacheSpacename[namespace]:cacheSpacename[namespace]={}; offlineCache=cache["offlineCache"]?cache["offlineCache"]:cache["offlineCache"]={}; ret={ listen:function(key,fn){ _listen(key,fn,cache); //判断离线缓存是否存在 if(!offlineCache[key]||offlineCache[key].length==0){ return false; } _unshift.call(arguments,offlineCache); _trigger.apply(this,arguments); //离线缓存只是用一次 delete offlineCache[arguments[1]]; }, trigger:function(){ var args, _self=this; _unshift.call(arguments,cache); args=arguments; var e=_trigger.apply(_self,args); if(e){ return false; } //使用闭包的访问到离线前的数据参数 var fn=function(){ _trigger.apply(_self,args); } //离线缓存 if(!offlineCache[args[1]]){ offlineCache[args[1]]=[]; } offlineCache[args[1]].push(fn); }, remove:function(key,fn){ _remove(key,fn,cache); } }; return ret; } return { create:_create, listen:function(key,fn){ var event=this.create(); event.listen(key,fn); }, trigger:function(){ var event=this.create(); event.trigger.apply(this,arguments); }, remove:function(key,fn){ var event=this.create(); event.remove(key,fn); } } } return event(); })(); /*测试*/ function a(a){ console.log(a); } function b(a){ console.log("b:"+a); } Event.trigger("c","default"); Event.listen("c",a); Event.create("zhang").trigger("c","abc"); Event.create("zhang").listen("c",a); Event.create("zhang").trigger("c","abcaa"); Event.create("zhang").trigger("c","abcxxx"); Event.create("zhanga").trigger("c","abc"); Event.create("zhanga").listen("c",a); Event.create("zhanga").listen("c",b); Event.create("zhanga").remove("c",a); Event.create("zhanga").trigger("c","abcxxss");