观察者模式

观察者模式(Observer):

又被称为发布-订阅者模式或消息机制,定义了一种依赖关系,解决了主体对象与观察者之间功能的耦合。

创建一个观察者对象

首先我们创建一个闭包对象,让其在页面加载就立即执行

var Observer=(function(){
//为了防止消息队列暴露而被篡改,我们将消息容器设置为静态私有变量
var _messages={};
return{
//注册信息接口
regist:function(){},
//发布信息接口
fire:function(){},
//移除信息接口
remove:function(){}
}
})();

现在我们观察者对象的雏形创建出来了,接着我们就要一一实现这三个方法,我们首先实现消息注册方法

注册方法的作用是将订阅者注册的消息退路到消息队列中,因此我们需要接受两个参数:消息类型以及相应的处理动作,在退路到详细队列时如果此消息不存在则应该创建一个该消息类型并将消息放入消息队列中,如果此消息存在则应该将消息执行方法推入该消息对应的执行方法队列中,这么做的目的也是保证多个模块注册同一则消息时能顺利执行。

//注册信息接口
regist:function(type,fn){
//如果此消息不存在我们创建一个该消息类型
if(typeof _messages[type]==='undefined'){
//将动作推入到该消息对应的动作执行队列中
_messages[type]=[fn];
}else{
//如果此消息存在,我们将动作方法推入该消息对应的动作执行序列中
_message[type].push(fn);
} }

接下来我们实现发布信息的方法对于发布信息的方法,其功能是当观察者发布一个消息时将所有订阅者订阅的消息一次执行。所以应该接收两个参数,消息类型以及动作执行时需要传递的参数,当然在这里消息类型是必须的。在执行消息动作队列之前校检信息的存在是很有必要的,然后遍历消息执行方法队列并依次执行。然后将消息类比一级传递的参数打包后一次传入消息执行方法中

//发布信息接口
fire:function(type,args){
//如果该消息没被注册则返回
if(!_messages[type]) return;
//定义消息信息
var events={
type:type, //消息类型
args:args||{} //消息携带数据
},
i=0,
len=_messages[type].length;
//遍历消息动作
for(var i=0;i<_messages[type].length;i++){
//一次执行注册的消息对应的动作序列
_messages[type][i]call(this,events);
}
}

最后我们来实现移除消息方法,移除消息方法,其功能是将订阅者注销的消息从消息队列中清楚,因此我们也需要两个参数,即消息类型以及执行的某一动作。当然在删除消息时我们也需要校检消息是否存在

//移除消息接口
remove:function(type,fn){
//如果消息动作队列对象存在
if(_messages[type] instanceof Array){
//从最后一个消息动作遍历
for(var i=_messages[type].length-1;i>=0;i--){
//如果存在该动作真在消息动作序列中移除相应动作
_messages[type][i]===fn&&_messages[type].splice(i,1);
}
}
}

好了现在我们的观察者对象创建成功了,具体结构如下

var Observer=(function(){
//为了防止消息队列暴露而被篡改,我们将消息容器设置为静态私有变量
var _messages={};
return{
//注册信息接口
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(var i=0;i<_messages[type].length;i++){
//一次执行注册的消息对应的动作序列
_messages[type][i]call(this,events);
}
},
//移除信息接口
remove:function(type,fn){
//如果消息动作队列对象存在
if(_messages[type] instanceof Array){
//从最后一个消息动作遍历
for(var i=_messages[type].length-1;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:'传递参数'});

我们来看一看结果

接着我们在用一个具体的例子来展示观察者对象的作用。

我们首先创建公司类,公司是被提需求的对象,因此当它们是订阅者。同时公司也有对需求分析,以及沟通需求的动作

//公司类
var company=function(demand){
var that=this;
//这是一个需求
that.demand=demand;
//沟通需求动作
that.communication=function(){
console.log(that.demand);
}
}

当然我们也需要对需求分析,我们再添加一个分析方法

company.prototype.analysis=function(demand){
//我们注册一下
Observer.regist(demand,this.communication);
}

当然还有需求沟通失败的情况,谈崩了那就没得谈了,所以我们再加一个失败的方法

company.prototype.breakdown=function(demand){
console.log(this.demand+''+demand+'谈判失败')
//取消对需求的监听
Observer.remove(demand,this.communication);
}

既然有了公司类,那么我们还需要创建一个客户类,他会像公司提出需求,所以它有提出需求的动作

//客户类
var client=function(){};
//客户提需求的方法
client.prototype.ask=function(demand){
console.log("需求是:"+demand);
//发布需求消息
Observer.fire(demand);
}

现在我们公司类和客户类都创建完成了,接下来我们创建3家需要参与竞标的公司

var company1=new company("第一家公司沟通需求");
var company2=new company("第二家公司沟通需求");
var company3=new company("第三家公司沟通需求");

然后我们分别获取客户需求

company1.analysis("我需要一个门户网站");
company2.analysis("我需要一款CRM软件");
company3.analysis("我需要一个商城项目");
company3.analysis("我需要一个门户网站");

然后我们第三家公司门户网站项目沟通失败了

company3.breakdown("我需要一个门户网站");

接着我们创建一个客户类

var client=new client();

客户提出了三个需求

client.ask("我需要一个门户网站");
client.ask("我需要一款CRM软件");
client.ask("我需要一个商城项目");

好了我们调用来看一下

总结

观察者模式最主要的作用是解决类或对象之间的耦合,解耦两个相互依赖的对象,使其依赖于观察者的消息机制这样对于任意一个订阅者对象来说,其他订阅者对象的改变不会影响到自身。对于每一个订阅者来说,其自身既可以是消息的发出者也可以是消息的执行者,这都依赖于调用观察者对象的三种方法(订阅消息,注销消息,发布消息)中的某一种。

在实际应用中观察者模式适用于团队开发,当要完成一个涉及多模块调用的需求,观察者模式的优势就显而易见了,模块间的信息传递不必要相互引用其他模块,只需要通过观察者模式注册或者发布消息即可。

也谢谢大家看到这里:)如果你觉得我的分享还可以请点击推荐,分享给你的朋友让我们一起进步~

好了以上就是本次分享的全部内容,本次示例参考自JavaScript设计模式一书,让我们一点点积累一点点成长,希望对大家有所帮助。

欢迎转载,转载请注明作者,原文出处。

再起航,我的学习笔记之JavaScript设计模式18(观察者模式)的更多相关文章

  1. 再起航,我的学习笔记之JavaScript设计模式02

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 我们 ...

  2. 再起航,我的学习笔记之JavaScript设计模式01

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 在通 ...

  3. 再起航,我的学习笔记之JavaScript设计模式03

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 上一 ...

  4. 再起航,我的学习笔记之JavaScript设计模式04

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 上回 ...

  5. 再起航,我的学习笔记之JavaScript设计模式05(简单工程模式)

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前几 ...

  6. 再起航,我的学习笔记之JavaScript设计模式06(工厂方法模式)

    上一次已经给大家介绍了简单工厂模式,相信大家对创建型设计模式有了初步的了解,本次我将给大家介绍的是工厂方法模式. 工厂方法模式 工厂方法模式(Factory Method):通过对产品类的抽象使其创建 ...

  7. 再起航,我的学习笔记之JavaScript设计模式05(简单工厂模式)

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前几 ...

  8. 再起航,我的学习笔记之JavaScript设计模式06(抽象工厂模式)

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前两 ...

  9. 再起航,我的学习笔记之JavaScript设计模式08(建造者模式)

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前几 ...

随机推荐

  1. HH去散步[SDOI2009]

    题目描述 HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离. 但是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回. 又因为HH是个喜欢 ...

  2. 七月SSL行业新闻回顾

    大事件一:被泄露的私钥和基于假私钥进行的撤回 上个月,我们报告说Spotify和Cisco在应用程序中捆绑了有效证书的私钥.这些证书将根据基准要求被撤销,但应用程序不是泄露私钥的唯一来源.Koen R ...

  3. oracle 基础(上)

    sql  语句氛围以下三种类型: dml:Date Manipulation Language 数据操纵语言 DDL:Data Definition Language 数据定义语言 DCL:Data ...

  4. 金明的预算方案 NOIP 2006 提高组

    题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:"你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱 ...

  5. HDU 6006 Engineer Assignment:状压dp

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6006 题意: 在Google中,有个n项目,m个专家.第i个项目涉及c[i]个领域,分别为a[i][0 ...

  6. python_正则表达式

    re.match函数 re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none. 函数语法: \[re.match(pattern, strin ...

  7. db2 表关联查询

    今天在MapReduce的练习中看到了一个题目: file: CHILD PARENT ---------- ---------- tom lucy tom jack jone lucy jone j ...

  8. python基础之数据类型与变量

    一.变量 1 什么是变量之声明变量 #变量名=变量值age=18gender1='male' gender2='female' 2 为什么要有变量 变量作用:"变"=>变化, ...

  9. “selection does not contain a main type”解决方法

    在运行java程序时,出现了错误“selection does not contain a main type”. 是因为.java文件不在项目的src路径内,也就是说源代码未被eclipse编译,字 ...

  10. Bresenham画椭圆算法

    这里不仔细讲原理,只是把我写的算法发出来,跟大家分享下,如果有错误的话,还请大家告诉我,如果写的不好,也请指出来,一起讨论进步. 算法步骤: (1) 输入椭圆的长半轴a和短半轴b. (2) 计算初始值 ...