再起航,我的学习笔记之JavaScript设计模式18(观察者模式)
观察者模式
观察者模式(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(观察者模式)的更多相关文章
- 再起航,我的学习笔记之JavaScript设计模式02
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 我们 ...
- 再起航,我的学习笔记之JavaScript设计模式01
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 在通 ...
- 再起航,我的学习笔记之JavaScript设计模式03
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 上一 ...
- 再起航,我的学习笔记之JavaScript设计模式04
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 上回 ...
- 再起航,我的学习笔记之JavaScript设计模式05(简单工程模式)
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前几 ...
- 再起航,我的学习笔记之JavaScript设计模式06(工厂方法模式)
上一次已经给大家介绍了简单工厂模式,相信大家对创建型设计模式有了初步的了解,本次我将给大家介绍的是工厂方法模式. 工厂方法模式 工厂方法模式(Factory Method):通过对产品类的抽象使其创建 ...
- 再起航,我的学习笔记之JavaScript设计模式05(简单工厂模式)
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前几 ...
- 再起航,我的学习笔记之JavaScript设计模式06(抽象工厂模式)
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前两 ...
- 再起航,我的学习笔记之JavaScript设计模式08(建造者模式)
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前几 ...
随机推荐
- redis 一般性使用概述
最近一段时间与redis接触比较频繁.发现有些东西还是工作中经常会用到的,自己也花了点时间巩固下.本篇文章主要是以总结性的方式梳理,因为redis的主题很大,任何一个技术点展开都是几篇文章的量.也可以 ...
- Hexo博客添加SEO-评论系统-阅读统计-站长统计
原文地址:→传送门 写在前面 在五月出捣腾了一把个人博客,但是刚开始只做了一些基础设置,套路也没摸清,基础安装篇请看hexo从零开始到搭建完整,里面讲到了基础工具的安装及blog项目的文件夹含义,以及 ...
- Wordpress解析系列之PHP编写hook钩子原理简单实例
Wordpress作为全球应用最广泛的个人博客建站工具,有很多的技术架构值得我们学习推敲.其中,最著名最经典的编码技术架构就是采用了hook的机制. hook翻译成中文是钩子的意思,单独看这个词我们难 ...
- Spring初始化ApplicationContext线程托管实际运用架构构思
今天我分享一个技术点,利用Spring初始化+线程接管进行程序启动后保持会话状态. 先来一段@test单元测试注解,后台开发的很熟悉,这是测试局部代码用的: @RunWith(SpringJUnit4 ...
- Python爬虫从入门到放弃(二十一)之 Scrapy分布式部署
按照上一篇文章中我们将代码放到远程主机是通过拷贝或者git的方式,但是如果考虑到我们又多台远程主机的情况,这种方式就比较麻烦,那有没有好用的方法呢?这里其实可以通过scrapyd,下面是这个scrap ...
- httpd页面用户访问认证控制
[root@liubin-pc ~]# yum install -y httpd 客户机地址限制 通过配置Order.Deny from.Allow from 来限制客户机 allow.deny :先 ...
- XAMPP启动MySQL时报端口被占用错误
1 问题描述 启动XAMPP中的mysql时宝凑,错误信息是:3306端口被占用 2 问题诊断 2.1 可能是在安装XAMPP之前安装了MySQL,这样电脑中就有两个MySQL啦,如果我们在安装时都采 ...
- RabbitMQ --- Publish/Subscribe(发布/订阅)
目录 RabbitMQ --- Hello Mr.Tua RabbitMQ --- Work Queues(工作队列) 前言 在第二篇文章中介绍了 Work Queues(工作队列),它适用于把一个消 ...
- mac系统读写NTFS格式的移动硬盘
Mac OS X默认情况下,对于NTFS的移动硬盘只能读不能写,可通过将移动硬盘格式化为exFAT 或者安装NTFS相关的工具,达到可读写的目的,但对于系统的安全性方面,以上方式得不到保证:在Mac ...
- IOS中的单例设计模式
单例设计模式是IOS开发中一种很重要很常用的一种设计模式.它的设计原理是无论请求多少次,始终返回一个实例,也就是一个类只有一个实例.下面是苹果官方文档中关于单例模式的图片: 如图所示,左边的图是默认的 ...