日期:2014-05-16  浏览次数:20371 次

Javascript 的函数式对象(九)利用事件机制降低耦合

?

大多数简单的页面应用,js可以写得较为自由随意,但涉及对象众多,关系复杂的 One Web Page 企业应用,

?

常常需要考虑如何降低js对象间的耦合,那种“你中有我,我中有你”的代码编写方式,会让维护人员无法接手。

?

因为工作关系,我曾接手过一个包含数千行js代码的项目。该项目的开发团队在国外,当时也还处于开发阶段。

?

缺少文档,遇到问题只能发邮件询问国外的开发团队,时间长了他们也烦,只好自己静下心来看代码。

?

细看后发现js的架构共有三层:底层是dojo,中间是记录用户行为和通信的可配置框架,顶层是业务逻辑,

?

项目中的js文件全部由 eval 方法以字符串的形式动态加载到浏览器上运行,无法用firebug或chrome调试。

?

所幸的是这些js代码采用了函数式的面向对象编程风格,结构清晰,包含了众多模板和动态加载的配置文件。

?

因此我才能较快地掌握代码的来龙去脉。低耦合的代码有助于维护和扩展,这是大多数人的共识。

?

对于没有接口,没有继承,没有抽象类的js来说,“事件机制”能简单有效地降低js模块间的耦合。

?

下面的代码是“事件”机制的示例:

?

?

//定义树节点
var TreeItem = function(){
	
	//利用闭包创建的私有数组,用来保存所有监听树节点展开状态变化的事件
	var expandingChangedListenerList = [];
	var expanded = false;
	
	//私有方法:当树节点的展开状态发生变化时,调用监听器数组中的所有监听方法
	function fireExpandingChangedEvent(){
		//遍历数组中的所有监听器
		for(var i=0; i<expandingChangedListenerList.length; i++){			
			var listener = expandingChangedListenerList[i]; 

			var event = { //创建事件对象,包含调用监听方法所需要的参数
				button: 0
			}
			listener(event); // 执行监听器方法
		}
	}
	//公共方法:向树节点对象添加监听事件
	this.addExpandingChangedListener = function(listener){
		if(listener && typeof(listener) == 'function'){
			expandingChangedListenerList.push(listener);
		}
	}
	//公共方法:当树节点的展开状态变化时,调用所有的监听器方法
	this.setExpanded = function(isExpanded){
		if(expanded != isExpanded){
			expanded = isExpanded;
			fireExpandingChangedEvent();
		}
	}
}
/////////////////////////////////////

//实例化一个树节点
var treeItem = new TreeItem();

// 定义一个属性窗口对象,向树节点treeItem注册一个展开状态变化监听器
// 如果树节点的展开属性发生变化,属性窗口会输出一段字符
var PropertiesView = {
	init: (function(){
		var listener = function(event){
			if(event && event.button == 0){
				console.log('Do something here for the changed status of tree item.');
			}
		};	
		treeItem.addExpandingChangedListener(listener);
	})()
};

// 改变树节点treeItem的展开状态,则注册其上的所有监听器将被调用
treeItem.setExpanded(true);
?

上述“事件机制”将不同模块间的事件响应代码留在自身,被监听者不需要知道谁在监听它,也不需知道监听者随后的行动。

?

而监听者也不需要知道自己定义的监听方法在什么时候,以及在何种情况下会被调用,只要注册到正确的对象上即可。

?

事件的相关各方都减少了对对方知识的掌握,从而降低了耦合。

?

需要注意的是,“零耦合”的情况是不存在的:上述代码中,作为事件监听者的PropertiesView,

?

以及作为事件被监听者的treeItem,二者间仅有的耦合性,集中在event 对象上。

?

因此各类 Event 事件对象,需要在代码的显要部分,公开地定义出来,以供查阅修改,甚至使用模板或配置文件?。

?

这也正是所有解耦合设计的终极目地:将耦合暴露在阳光下,而不是隐藏在阴暗的角落。

?

?

?

1 楼 bigbighead 2010-11-18  
赞最后一句话  Yeah!