观察者模式(observer):又被称为 发布-订阅者模式或者消息机制,定义了一种依赖关系,解决了主体对象与观察者之间功能耦合。
一、这样的需求
在实现自己的需求,而添加一些功能代码,但是又不想新添加的代码影响他人的实现功能,也就是说,你不想让自己的模块与他人的模块严重耦合在一起,对于这类问题,观察者模式是比较理想的解决方案。
观察者模式可以解开我和他们之间的功能耦合。
观察者模式,也被人称为消息机制或者发布-订阅者模式。为了解决主体对象与观察者之间功能耦合
二、创建一个观察者
把观察者或者消息系统看作一个对象,那么他应该包含两个方法,一个是接受消息,一个是向中转站发送相应消息。
首先,我们把需要的把观察者对象创建出来,有一个消息容器和三个方法,分别是,订阅消息的方法,取消订阅消息的方法,发送订阅消息的方法。
var Observer = (function(){ //防止消息队列暴露而被篡改,所以将消息容器作为静态私有变量保存 var __messsages = {}; return { //注册信息接口 regist: function(){}, //发布信息的接口 fire: function(){}, //移除信息接口 remove: function(){} } })();
观察者对象的雏形出来了,我们需要做的事情就是实现这三个方法,我们首先实现消息注册方法,注册方法的作用是将订阅者注册的消息推入到消息队列中,因此我们需要接受两个参数:消息类型和以及相应的处理动作,在推入到消息队列时如果此消息不存在应该创建一个该消息类型并将该消息放入到消息队列中,如果此消息存在则应该将消息执行方法推入该消息对应的执行方法队列中,这么做目的是保证多个模块注册同一个消息能顺利执行。
regist: function(type,fn){ //如果此消息不存在,则应该创建一个该消息类型 if(typeof __messages[type] === 'undefined'){ //将对象推入到该消息对应的动作执行队列中 __messages[type] = [fn]; //如果此消息存在 }else{ //将动作方法推入该消息对应的动作执行序列中 __messages[type].push(fn); } }
对于发布消息方法,其功能是当观察者发布一个消息时将所有订阅者订阅的消息一次执行。
故应该接受两个参数,消息类型以及动作执行时需要传递的参数,当然在这里消息类型是必须的。在执行消息队列之前校验消息的存在是很有必要的。
然后遍历消息执行方法队列,并依此执行。
然后将消息类别以及传递的参数打包后依次传入消息队列执行方法中。
fire: function(type,args){ //如果该消息没有被注册,则返回 if(!__messages[type]){ return ; //定义消息信息 var events = { type:type, args:args||{} }, i=0; len = __messages[type].length; for(;i<len;i++){ //依次执行注册的消息对应的动作序列 __messages[type][i].call(this,events); } }
最后是消息注册方法,其功能是将订阅者注销的消息从消息队列清除,因此我们也需要两个参数,即消息类型以及执行的某一个动作。当然为了避免删除消息动作时消息不存在情况的出现,对消息队列中消息的存在性校验也很有必要的。
remove:function(type,fn){ //如果消息动作队列存在 if(__messages[type] instanceof Array){ //从最后一个消息动作遍历 var i = __messages[type].length-1; for(;i>=0;i--){ //如果存在该动作则在消息动作中移除相应动作 __messages[type][i] === fn && __messages[type].splice(i,1); } } }
三、测试一下
观察者对象或者说,消息系统创建成功之后,简单测试一下:
首先订阅一条消息
Observer.regist('test',function(e){ console.log(e.type,e.args.msg); });
然后,我们发布这则消息
Observer.fire('test',{msg:'传递参数'}); //test 传递参数
四、回忆一下
观察者模式最主要的作用的:解决类或对象之间的耦合,解耦两个相互依赖的对象,使其依赖于观察者的消息机制。
这样对于任意一个订阅者对象来说,其他订阅者对象的改变不会影响到自身。
对于每一个订阅者来说,其自身既可以是消息的发出者也可以是消息的执行者,这都依赖于调用观察者对象的三个方法(订阅消息,注销消息,发布消息)