这里拿给DOM元素设置样式的case来演绎在js中怎么使用Facade Pattern。
先看菜鸟的做法:
var element1 = document.getElementById('foo');
element1.style.color = 'red';
var element2 = document.getElementById('bar');
element2.style.color = 'red';
var element3 = document.getElementById('baz');
element3.style.color = 'red';
element1.style.color = 'red';
var element2 = document.getElementById('bar');
element2.style.color = 'red';
var element3 = document.getElementById('baz');
element3.style.color = 'red';
为了给3个元素增加红色,重复做了3次。
为了不重复造车轮,做一下抽象总结,简化如下:
function setStyle(elements, prop, val) {
for ( var i = 0, len = elements.length - 1; i < len; ++i) {
document.getElementById(elements[i]).style[prop] = val;
}
}
setStyle(['foo', 'bar', 'baz'], 'color', 'red');
for ( var i = 0, len = elements.length - 1; i < len; ++i) {
document.getElementById(elements[i]).style[prop] = val;
}
}
setStyle(['foo', 'bar', 'baz'], 'color', 'red');
再看现在的调用方式,仅一行setStyle即可。再多的元素,只要需要设置的样式key/value相同,一行代码全部搞定。
但是,这还不够。现实情况是,程序可能需要一次性给一个或多个元素设置多个样式属性。基于css的表现形式,继续改进如下:
function setCSS(el, styles) {
for ( var prop in styles) {
if (!styles.hasOwnProperty(prop)) continue;
setStyle(el, prop, styles[prop]);
}
}
setCSS(['foo', 'bar', 'baz'], {
position: 'absolute',
top: '50px',
left: '300px'
});
for ( var prop in styles) {
if (!styles.hasOwnProperty(prop)) continue;
setStyle(el, prop, styles[prop]);
}
}
setCSS(['foo', 'bar', 'baz'], {
position: 'absolute',
top: '50px',
left: '300px'
});
看看setCSS那一行,后边的属性键值对们是不是很css? 如此好的一个Facade应用,以后可以只对外公开setCSS方法了。
下面是为了解决霸道的IE和基于DOM标准的浏览器的差别,利用Facade来解决一致性的问题的示例:
DED.util.Event = {
getEvent: function (e) {
return e || window.event;
},
getTarget: function (e) {
return e.target || e.srcElement;
},
stopPropagation: function (e) {
if (e.stopPropagation) {
e.stopPropagation();
}
else {
e.cancelBubble = true;
}
},
preventDefault: function (e) {
if (e.preventDefault) {
e.preventDefault();
}
else {
e.returnValue = false;
}
},
stopEvent: function (e) {
this.stopPropagation(e);
this.preventDefault(e);
}
};
/* test */
addEvent($('example'), 'click', function (e) {
// Who clicked me.
console.log(DED.util.Event.getTarget(e));
// Stop propagating and prevent the default action.
DED.util.Event.stopEvent(e);
});
getEvent: function (e) {
return e || window.event;
},
getTarget: function (e) {
return e.target || e.srcElement;
},
stopPropagation: function (e) {
if (e.stopPropagation) {
e.stopPropagation();
}
else {
e.cancelBubble = true;
}
},
preventDefault: function (e) {
if (e.preventDefault) {
e.preventDefault();
}
else {
e.returnValue = false;
}
},
stopEvent: function (e) {
this.stopPropagation(e);
this.preventDefault(e);
}
};
/* test */
addEvent($('example'), 'click', function (e) {
// Who clicked me.
console.log(DED.util.Event.getTarget(e));
// Stop propagating and prevent the default action.
DED.util.Event.stopEvent(e);
});
至此,可以看出Facade Pattern的主要作用不外2点:1)外外围程序提供比较简洁、人性化的接口;2)内部封装解决一些差异性,是外部看来是一致的。 源码download