天天看點

【JavaScript設計模式】行為型設計模式--釋出-訂閱模式

       釋出-訂閱者模式又叫觀察者模式,它定義對象間的一種一對多的依賴關系,當一個對象的狀态發生改變時,所有依賴于它的對象都将得到通知。那具體的含義,就是有訂閱者和釋出者,兩者的功能,訂閱是請求在某些事件(event)到達時可以通知它并執行對應的動作(action),而釋出則相對的是向訂閱告知事件(event)已經到達,你可以執行對應的動作(action)了。

      舉一個現實中的例子。我們在現實中可以看到一個現象:假如A要去買房子,那麼這個A就會把自己的聯系方式給售樓處的負責人,隻要樓盤有房子就會通知A,然後A就可以過去看房子,買房子,就不需要每天都去打電話詢問是不是到了購買時間,這個例子就可以利用釋出-訂閱者模式進行實作,這個售樓處負責人是釋出者,A是訂閱者,接下來我們實作簡單的釋出-訂閱者模式。

代碼如下:

var salesOffices = {};//定義售樓處
salesOffices.clientList = [];//緩存清單,存放訂閱者的回調函數
//增加訂閱者
salesOffices.listen = function ( fn ){
	this.clientList.push( fn );  //訂閱的消息存進緩存清單
};
//釋出消息
salesOffices.trigger = function (){
	for(var i = 0, fn; fn = this.clientList[ i++ ];){
		fn.apply(this,arguments);
	}
};
//A訂閱消息
salesOffices.listen( function(price, squareMeter){
	console.log('價格=' + price);
	console.log('squareMeter=' + squareMeter);
});
//B訂閱消息
salesOffices.listen( function(price, squareMeter){
	console.log('價格=' + price);
	console.log('squareMeter=' + squareMeter);
});

salesOffices.trigger(20000,88);//價格=20000  squareMeter=88
salesOffices.trigger(30000,110); //價格=30000  squareMeter=110
           

     以上代碼實作了最簡單的釋出-訂閱模式,但是這裡還存在一些問題。我們看到訂閱者接收到了釋出者釋出的每個消息,雖然A隻想買88平方米的房子,但是釋出者把110平方米的資訊也發給了A,這對于小明來說是不必要的,是以我們需要增加一個辨別key,讓訂閱者隻訂閱自己感興趣的消息。修改之後的代碼如下:

var salesOffices = {};//定義售樓處
salesOffices.clientList = [];//緩存清單,存放訂閱者的回調函數
//增加訂閱者
salesOffices.listen = function ( key, fn ){
	if( !this.clientList[ key] ){
		this.clientList[ key ] = [];  //如果還沒有訂閱過此類消息,給該類消息建立一個緩存清單
	}
	this.clientList[ key ].push( fn ); //訂閱的消息存進緩存清單
};
//釋出消息
salesOffices.trigger = function (){
	var key = Array.prototype.shift.call( arguments), //取出消息類型
		fns = this.clientList[ key ];

	if( !fns || fns.length === 0){  //如果沒有訂閱該消息,則傳回
		return false;
	}

	for(var i = 0, fn; fn = fns[ i++ ];){
		fn.apply(this,arguments);
	}
};

salesOffices.listen('squareMeter80', function(price){
	console.log('價格=' + price);
});

salesOffices.listen('squareMeter100', function(price){
	console.log('價格=' + price);
});

salesOffices.trigger('squareMeter80',20000);  //釋出88平方米房子的價格
salesOffices.trigger('squareMeter100',30000); //釋出110平方米房子的價格
           

     接下來實作一個通用的釋出-訂閱模式。

var event = {
	clientList : [],//緩存清單,存放訂閱者的回調函數
	//增加訂閱者
	listen : function ( key, fn ){
		if( !this.clientList[ key] ){
			this.clientList[ key ] = [];  //如果還沒有訂閱過此類消息,給該類消息建立一個緩存清單
		}
		this.clientList[ key ].push( fn ); //訂閱的消息存進緩存清單
	},
	//釋出消息
	trigger : function (){
		var key = Array.prototype.shift.call( arguments), //取出消息類型
			fns = this.clientList[ key ];

		if( !fns || fns.length === 0){  //如果沒有訂閱該消息,則傳回
			return false;
		}

		for(var i = 0, fn; fn = fns[ i++ ];){
			fn.apply(this,arguments);
		}
	},
	//取消訂閱
	remove : function(){
		var fns = this.clientList[ key ];

		if( !fns){
			return false;
		}
		if( !fn){
			fns && (fns.length = 0);
		}else{
			for(var l = fns.length - 1; l >= 0; l--){
				var _fn = fns[l];
				if(_fn === fn){
					fns.splice(l, 1);
				}
			}
		}
	}
};

//給所有的對象都動态安裝釋出-訂閱模式
var installEvent = function ( obj ){
	for( var i in event){
		obj[ i ] = event[ i ];
	}
}
 
           

測試:

var salesOffices = {};
var fn1,fn2;
installEvent(salesOffices);
//A訂閱消息
salesOffices.listen('squareMeter88', fn1 = function( price ){
	console.log('價格=' + price);
});
//B訂閱消息
salesOffices.listen('squareMeter88', fn2 = function( price ){
	console.log('價格=' + price);
});

salesOffices.remove('squareMeter88', fn1 );//删除A的訂閱
salesOffices.trigger('squareMeter88', 20000 );//價格=20000
           

繼續閱讀