观察者模式

观察者模式(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. 百度地图api将可视区域定位到当前所在位置

    1.前言 开头不说点什么,总是有点不习惯.还是说点什么吧,关于百度地图,我用的次数还是比较多的,没办法,需求呀.好吧,在用百度地图的时候,确实有过很多需求,不过好在百度地图很强大,每次需求在探索后都能 ...

  2. 使用Node.js搭建静态资源服务器

    对于Node.js新手,搭建一个静态资源服务器是个不错的锻炼,从最简单的返回文件或错误开始,渐进增强,还可以逐步加深对http的理解.那就开始吧,让我们的双手沾满网络请求! Note: 当然在项目中如 ...

  3. Eclipse、maven项目常见问题

    阿里云maven仓库地址: <mirror> <id>nexus-aliyun</id> <mirrorOf>*</mirrorOf> &l ...

  4. Python Socket 简单聊天室1

    这是第一版,最简单的,仅仅实现了通信,你收我发,我收你发而已.下篇将介绍,基于异步多线程的聊天室: 客户端: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ...

  5. Java中Scanner的输入流的关闭问题

    为什么Scanner in语句只能关闭一次,且对于其他任何Scanner输入流也关闭? 代码如图 import java.util.Scanner; public class ScannerTest  ...

  6. vue基础一

    一.vue的编写步骤 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...

  7. 微信小程序开发之微信支付

    微信支付是小程序开发中很重要的一个环节,下面会结合实战进行分析总结 环境准备 https服务器 微信小程序只支持https请求,因此需要配置https的单向认证服务(请参考 另一篇文章https受信证 ...

  8. NYOJ--128--前缀式计算(表达式求值)

    前缀式计算 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 先说明一下什么是中缀式: 如2+(3+4)*5这种我们最常见的式子就是中缀式. 而把中缀式按运算顺序加上括 ...

  9. hdu--3782--找规律--xxx定律

    /* Name: hdu--3782--xxx定律 Date: 17/04/17 21:34 Description: 找规律题,又想打表了 */ /* for(int i=2;i<30;++i ...

  10. json对象、构造原型、组合继承

    一.json对象套路 var stu = { "name": "龙姑娘", age: 16, classmate: { name: "李小玉" ...