门面模式

优质
小牛编辑
137浏览
2023-12-01

当我们提出一个门面,我们要向这个世界展现的是一个外观,这一外观可能藏匿着一种非常与众不同的真实。这就是我们即将要回顾的模式背后的灵感——门面模式。这一模式提供了面向一种更大型的代码体提供了一个的更高级别的舒适的接口,隐藏了其真正的潜在复杂性。把这一模式想象成要是呈现给开发者简化的API,一些总是会提升使用性能的东西。

门面是一种经常可以在Javascript库中看到的结构性模式,像在jQuery中,尽管一种实现可能支持带有广泛行为的方法,但仅仅只有这些方法的“门面”或者说被限制住的抽象才会公开展现出来供人们所使用。

这允许我们直接同门面,而不是同幕后的子系统交互。不论何时我们使用jQuery的$(el).css或者$(el).animate()方法,我们实际上都是在使用一个门面——更加简单的公共接口让我们避免为了使得行为工作起来而不得不去手动调用jQuery核心的内置方法。这也避免了手动同DOM API交互和维护状态变量的需要。

应该考虑对jQuery的核心方法做一层中间抽象。对于开发者来说更直接的负担是DOM API,而门面使得jQuery使用起来如此的容易。

为了在我们所学的基础上进行构建,门面模式同时需要简化一个类的接口,和把类同使用它的代码解耦。这给予了我们使用一种方式直接同子系统交互的能力,这一方式有时候会比直接访问子系统更加不容易出错。门面的优势包括易用,还有常常实现起这个模式来只是一小段路,不费力。

让我们通过实践来看看这个模式。这是一个没有经过优化的代码示例,但是这里我们使用了一个门面来简化跨浏览器事件监听的接口。我们创建了一个公共的方法来实现,此方法 能够被用在检查特性的存在的代码中,以便这段代码能够提供一种安全和跨浏览器兼容方案。

var addMyEvent = function( el,ev,fn ){
	if( el.addEventListener ){
		el.addEventListener( ev,fn, false );
	}else if(el.attachEvent){
		el.attachEvent( "on" + ev, fn );
	} else{
		el["on" + ev] = fn;
	}
};

我们都熟知jQuery的$(document).ready(..),使 用了一种类似的方式。在内部,这实际上是考一个叫做bindReady()的方法来驱动的,它做了一些这样的事:

bindReady: function() {
	if ( document.addEventListener ) {
		// Use the handy event callback
		document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
		// A fallback to window.onload, that will always work
		window.addEventListener( "load", jQuery.ready, false );
		// If IE event model is used
	} else if ( document.attachEvent ) {
		document.attachEvent( "onreadystatechange", DOMContentLoaded );
		// A fallback to window.onload, that will always work
		window.attachEvent( "onload", jQuery.ready );

这是门面的另外一个例子,其它人只需要使用被$(document).ready(...)有限暴露的简单接口,而更加复杂的实现被从视野中隐藏了。

门面不仅仅只被用在它们自己身上,它们也能够被用来同其它的模式诸如模块模式进行集成。如我们在下面所看到的,我们模块模式的实体包含许多被定义为私有的方法。门面则被用来提供访问这些方法的更加简单的API:

var module = (function() {
	var _private = {
		i:5,
		get : function() {
			console.log( "current value:" + this.i);
		},
		set : function( val ) {
			this.i = val;
		},
		run : function() {
			console.log( "running" );
		},
		jump: function(){
			console.log( "jumping" );
		}
	};
	return {
		facade : function( args ) {
			_private.set(args.val);
			_private.get();
			if ( args.run ) {
				_private.run();
			}
		}
	};
}());
// Outputs: "current value: " and "running"
module.facade( {run: true, val:} );

在这个示例中,调用module.facade()将会触发一堆模块中的私有方法。但再一次,用户并不需要关心这些。我们已经使得对用户而言不需要担心实现级别的细节就能消受一种特性。