观察者模式又叫做发布-订阅模式,主要功能就是解耦。
有一个实际需求,在新闻评论模块,
当我们发布评论时,会在展示评论末尾处追加最新评论,同时消息模块的消息数量也会增加,删除评论留言区的评论时,消息模块数量相应的减少。
但是在项目开发中,往往不同模块由不同的人开发,消息模块,评论模块分别由不同的人开发。这个时候不想将几个独立的模块整合在一起,可以用观察者模式。
下面我们一步一步实现:
1.创建观察者:观察者对象包含一个消息容器,和三个方法,订阅消息方法,取消订阅方法,发送消息的方法。
(1) 注册消息方法作用是将订阅者的消息推送到消息队列中,因此我们需要接受两个参数,消息类型和相应的处理动作。
(2)发布消息功能是当观察者发布一个消息时,将所有消息一次执行。接受两个参数,消息类型和动作执行时需要传进来的参数。
(3)注销消息功能是将消息从消息队列中移除
//将观察者放到闭包中,当页面加载就立即执行
var Observer=(function(){
//消息容器
var _message={};
return {
//注册消息
register:function(type,fn){
//如果消息类型不存在,则创建一个该消息类型,并将该动作推到类型中
if(typeof _message[type]==='undefined'){
_message[type]=[fn];
}else {
//如果消息类型存在,直接将动作推到相应的类型中
_message[type].push(fn);
}
};
//发布消息
fire:function(type,args){
if(!_message[type])
return;
var event={
type:type,
args:args||{}
},
i=0,
len=_message[type].length;
//依次执行注册的消息对应的动作序列
for(;i<len;i++){
_message[type][i].call(this,event)
}
};
//移除消息
remove:function(type,fn){
if(_message[type] instanceof Array){
var len=_message[type].length-1;
for(;len>0;len--){
_message[type][i]==fn && _message[type].splice(i,1);
}
}
};
}
})()
观察者对象创建后,我们可以先简单测试一下:
首先订阅一条消息:
Observer.register('test',function(){
console.log(e.type,e.args.msg)};
)
然后发布一条消息:
Observer.fire('test',{msg:"传递参数"});// test 传递参数
回到我们开始说的需求中,对于追加留言的功能应该放到留言模块中,对于消息增减应该放到消息模块中,留言的提交放到提交模块中。选择用观察者模式开发,首先分析哪些模块注册消息,哪些模块发布消息,发布留言和删除留言是用户主动出发,所以应该是观察者主动发布消息,评论的追加和消息增加是被动触发,所以他应该是观察者订阅消息,这样的话,用户信息模块既是信息的发布者也是信息的接收者,提交模块是发送者,浏览模块是信息的接收者。
function $(id){
return document.getElementById(id);
}
//工程师A
(function(){
function addMsgItem(e){
var text = e.args.text,
ul = $('msg'),
li = document.createElement('li'),
span = document.createElement('span');
li.innerHTML = text;
//关闭按钮
ul.removeChild(li);
//发布删除留言消息
Observer.fire('removeCommentMessage',{num:-1});
li.appendChild(span);//添加删除按钮
ul.appendChild(li);//添加留言节点
}
//注册添加评论信息
Observer.register('addCommentMessage',addMsgItem)
})()
//工程师B
(function(){
function changeMsgNum(e){
var num = e.args.num;
$('msg_num').innerHTML = parseInt($('msg_num').innerHTML)+num;
}
//注册添加评论消息
Observer.register('addCommentMessage',changeMsgNum)
.register('removeCommentMessage',changeMsgNum);
})()
//工程师C
(function(){
$('user_submit').onclick = function(){
//获取用户输入框中输入的消息
var text = $('user_input');
if(text.value === ''){
return;
}
//发布一则评论消息
Observer.fire('addCommentMessage',{
text:text.value,
num:1
});
text.value='';//将输入框置为空
}
})();