观察者模式

观察者模式(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. 使用pdfbox分页保存pdf为图片

    一.背景 pdfbox作为Apache开源的PDF操作工具,允许创建新的PDF文档,操作现有文档,以及从文档中提取内容的能力.Apache PDFBox还包括一些命令行实用工具.本文楼主主要介绍其中的 ...

  2. Java 方法重载,方法重写(覆盖),继承等细节注意

    1.方法重载(method overload)的具体规范 如果有两个方法的方法名相同,但参数不一致,那么可以说一个方法是另一个方法的重载. 一.方法名一定要相同. 二.方法的参数表必须不同,包括参数的 ...

  3. 华为服务器Linux在线做RAID方法

    背景概述 最近维护大数据的一些主机,大概有3k+的数目,有很大一部分是华为的服务器,大部分是12块数据盘,单盘做RAID0来存放数据,但是通常硬件是不可靠的,磁盘损坏是常态, 然而磁盘损坏进行定位更换 ...

  4. nginx实现wap移动端和PC端业务分离

    随着移动互联网时代的来临,很多WEB网站都已经推出了基于手机,Ipad等移动客户端的页面访问,这里介绍一下如何利用用户UA实现用户不同终端下的用户访问: $http_user_agent  为ngin ...

  5. 我的Chrome

    插件: CaretTab - New Tab Clock and Date 完全就是为了好看

  6. ecshop中的些assign_dynamic(’')

    很多做电子商务站的朋友都问我,在ecshop中,里面有个 assign_dynamic('index');这个到底是什么作用来的,这个其实是ecshop中的模板技术,动态处理一些局部信息更新而不被缓存 ...

  7. Kettle安装和配置

    0x01 Kettle软件概览 Spoon:集成开发环境 Kitchen:作业的命令行运行程序,可以通过Schell脚本来调用 Pan:转换的命令行程序 Carte:轻量级的HTTP服务,后台运行,监 ...

  8. python 第二天

    list,tuple,dict,set list,tuple,dict,set 是今天学习的四种 Python 内置类型 list list 即数组,有序的集合,可以使用索引的方式来访问 list 的 ...

  9. 轻量级ORM框架 QX_Frame.Bantina(二、框架使用方式介绍)

    轻量级ORM框架QX_Frame.Bantina系列讲解(开源) 一.框架简介 http://www.cnblogs.com/qixiaoyizhan/p/7417467.html 二.框架使用方式介 ...

  10. 调用Class.forName()要抛出异常

    今天学JDBC时,用到下面的程序: package bo; import java.sql.Connection; import java.util.ArrayList; import java.ut ...