天天看点

我理解的 js 的观察者模式 Observable

我第一次看 四人帮 写的《设计模式》时一头雾水,现在也是,或许其是针对专业的程序员学习使用的。

通过对Ext / Backbone 源码的学习,可总结如下:

模式 - 就是对解决某一类特定问题的有效总结,并在后续解决同样的问题可以持续使用。

设计模式 - 程序开发者认为自己是优雅的设计师。

观察者模式:主要应用于组件开发,以便组件使用者 可以自行定义某个性方法,在组件达到某种状态时调用。

一、观察者模式原理 

组件开发为了保证组件可以在不同的项目中都适用,其必须是对其常用功能 抽象出来 加以实现,绝不会包含具体的业务逻辑

而某一特定的项目使用者在其业务场景中使用组件时不可避免的要加入不同场景的业务逻辑,观察者模式很好的解决了这个的问题。

如下:

/*
* 组件开发者A 定义了通用方法 test 供 业务开发者B 使用
*/
function test(){
	beforeTest && beforeTest()
	// do test something
	console.log( 'test ');
	// do test something
	afterTest && afterTest();
}

/*
* 业务开发者B使用test,不修改test源码的情况下,执行test时,如B需要可以先执行B自定义的方法,test执行结束后还需再执行B自定义的另一方法
* 双方约定先执行的方法名为 beforeTest
*/
function beforeTest(){
	console.log('beforeTest')
	/*
	* do beforeTest something
	*/
}

/*
* 双方约定后执行的方法名为 afterTest
*/
function afterTest(){
	console.log('afterTest')
	/*
	* do afterTest something 
	*/
}

/*
* 示例:
*/
test();			//控制台会输出 开发者B自定义方法里的值  beforeTest ; afterTest 
//这样每次执行test时,都会执行B自定义的方法      

 通过上面这种模式,可以降低代码之间的耦合,据实际使用和双方约定,组件使用方可以在组件使用过程中,不对组件进行修改,却可以自由的定义、执行其业务需要的个性化的方法。

上面的方法只是讲了原理,并不适用于实际的项目,且对于beforeTest只能定义一个,经过简单的抽象提取如下

/*
* 组件开发者A定义组件 Person 供开发者B使用
*/
var events = {
	
	/*
	* 添加 name 对应的 事件callback,保存在this._events里
	*/
	on: function( name, callback ){
		this._events || (this._events = {});	//确保this._events存在
		this._events[name] = this._events[name] || [];//确保this._events[name]存在
		this._events[name].push({ callback: callback });//将callback 添加到 this._events[name] 内
	},
	
	/*
	* 执行已经保存在this._events里的 name 对应的事件
	*/
	trigger : function( name ){
		var args = Array.prototype.slice.call( arguments, 1 );		
                 //将参数中除 name 外的其它参数取出来
		if( this._events && this._events[name] ){					              //确保 this._events[name] 存在
			for( var i = 0; i < this._events[name].length; i++){	
                                //循环执行里面的callback
				this._events[name][i].callback.apply( this, args );	
                                //将步骤一取出的值作用callback的参数
			}
		}
	},
	
	/*
	* 删除this._events里 name 对应的事件
	*/
	off: function( name ){
		if( this._events ){
			delete this._events[name];							//删除  this._events[name]
		}
	}
}

var Person = function( config ){
	this.name = config.name;
	this.age = config.age;
}

for( var key in events ){
	Person.prototype[key] = events[key];		
        //将events里的属性添加到Person的原型里
}

Person.prototype.getName = function(){
	this.trigger('getName', this.name );
	return this.name;
}
/*
* 当设置Person里的名字里,执行一次 setName 监听
*/
Person.prototype.setName = function( name ){
	this.trigger( 'setName', name );
	this.name = name;
	return this;
}

/*
* 生成一个实例
*/
var lulu = new Person({ name: 'lulu', age: '25'});

/*
* 根据业务的个性化需要 添加一个监听事件,当执行到setName里,执行function,
*/
lulu.on( 'setName', function( name ){
	console.log('您执行一次setName __ ' + name );
	/*
	* 上面一个无意义的场景
	* 实际的场景可以是setName后会通过 ajax 保存到数据库等操作
	*/
});

/*
* 对同一个监听行为可以添加多个 事件
*/
lulu.on( 'setName', function( name ){
	console.log('您执行二次setName __ ' + name )
});

/*
* 测试
*/
lulu.setName('nana');		//控制台会输出	您执行一次setName __ nana;	您执行二次setName __ nana;
lulu.setName('aobama');		//您执行一次setName __ aobama;	您执行二次setName __ aobama;
lulu.off( 'setName' );
lulu.setName('lala');		//此时控制台将不会再输出信息      

 观察者模式 基础使用就是上面所讲,不过不同的框架又在此基础上丰富了功能

如backbone,可以一次添加多个监听事件 lulu.on({ 'setName': fun1, 'getName': fun2 }); 和只执行一次的监听  once。 具体参见 【backbone 源码分析】

Ext提供的功能更多,其中可以 据每次执行监听事件的结果是否为false来判定是否继续执行后面的操作, 除了这个功能较实用外,其它的功能个人认为不常用。

继续阅读