• 再起航,我的学习笔记之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设计模式一书,让我们一点点积累一点点成长,希望对大家有所帮助。

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

  • 相关阅读:
    关于微服务的协议概述
    Eclipse 安装阿里巴巴代码规范插件
    Eclipse安装LomBok插件
    关于JAVA架构师
    关于Object类的一些方法
    [SoapUI] SoapUI官方文档
    [Jmeter] 用xsltproc生成html格式的报告
    [RF] Robot Framework新手干货(转载)
    【SoapUI】比较Json response
    怎样查看一个端口有无开启
  • 原文地址:https://www.cnblogs.com/chen-jie/p/JavaScript-Observer.html
Copyright © 2020-2023  润新知