天天看點

我了解的 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來判定是否繼續執行後面的操作, 除了這個功能較實用外,其它的功能個人認為不常用。

繼續閱讀