一、前言
门面模式,也称Facade(外观)模式。核心的两点作用——
1> 简化类的接口(让接口变得更加容易理解、容易应用、更加符合对应业务),来掩盖一个非常不同或者复杂的实现
2> 消除类与使用它的客户代码之间的耦合
可以把门面模式想成是简化API来展示给其他开发人员,通常都是可以提高可用性。
二、举例
例子一、事件监听
function addEvent(el, type, fn) { if (window.addEventListener) { el.addEventListener(type, fn, false); } else if (window.attachEvent) { el.attachEvent('on' + type, fn); } else { el['on' + type] = fn; } }
有了这个门面,就有了一种为DOM节点添加事件监听器的简便方法,就不必再为每次为一个元素添加事件监听器都得针对浏览器间的差异进行检查而烦恼。
例子二、组合函数
function a(x) { // do stuff here... } function b(y) { // do stuff here... } function ab(x, y) { a(x); b(y); }
分别提供a、b和ab这几个函数可以获得更多粒度控制和灵活性。
例子三、工具函数
在处理跨浏览器的开发问题时,最好创建一些门面函数。如果涉及一个大型库,那么最好把其中所有的工具元素聚拢在一起,这样更好用,访问起来也更方便。鉴于各种浏览器在事件处理表现出来的大量差异,开发一个事件工具很有必要。
var DED = window.DED || {}; DED.util.Event = { getEvent: function(e) { return e || window.event; }, getTarget: function(e) { return e.target || e.srcElement; }, stopPropagation: function(e) { if (e.stopPropagation) { // W3 interface e.stopPropagation(); } else { // IE interface e.cancelable(); } }, preventDefault: function(e) { if (e.preventDefault) { // W3 interface e.preventDefault(); } else { // IE interface e.returnValue = false; } }, stopEvent: function(e) { DED.util.stopPropagation(e); DED.util.preventDefault(e); } };
应用上面的例子
addEvent($('#emample'), 'click', function(e) { // who clicked me console.log(DED.util.Event.getTarget(e)); // stop propagation and prevent the default action DED.util.Event.stopEvent(e); });
例子四、结合其他模式一起应用
门面模式不是必须单独使用。他们也可以与其他模式集成,如Module模式。Module模式的实例包含很多已经定义的私有方法。然后使用门面模式提供一个简单的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(); } } }; }());
三、适用场景
1.判断是否应该应用门面模式的关键在于辨认那些反复成组出现的代码。如果函数b出现在函数a之后这种情况经常出现,那么也许你应该考虑添加一个把这两个函数组合起来的门面函数。
2.在核心工具代码中加入门面函数的另一个目的是应对js内置函数在不同浏览器中的不同表现。这样做并不是因为不能直接使用这些API,而是因为在处理跨浏览器的差异问题时最好的解决方法就是把这些差异抽取到门面方法中。它们可以提供一个更一致的接口。
四、优势
1.门面模式的目的就是让程序员过得更轻松一些。编写一次组合代码,然后就可以反复使用它,这有助于节省时间和精力。它们可以替你把硬骨头啃掉,并且提供了一个处理常见问题和任务的简化接口。
2.门面方法方便了开发人员,并且提供了较高层的功能,如果不用门面模式的话,这些功能实现起来可能会乏味而又费力。
3.它们还能降低对外部代码的依赖程度,这为应用系统的开发增加了一些额外的灵活性。通过使用门面模式,可以避免与下层子系统紧密耦合。这样可以对这个系统进行修改而不会影响到客户代码。
五、劣势
有时候门面元素也会带来一些不必要的额外负担。方便的东西不一定就得用。门面模式常常被滥用。有时相比一个庞杂的门面函数,其组成函数在粒度方面更有吸引力。这是因为门面函数可能常常会执行一些你并不需要的任务。
六、结论
门面模式可用来创建便利函数,这些函数为执行各种复杂任务提供了一个简单的接口。它们使代码更容易维护和理解。它们还能弱化子系统和客户代码的耦合。便利方法有助于简化常见的重复性任务,以及把经常相伴出现的常用函数组合在一起。这个模式在DOM脚本编程这种需要面对各种不一致 的浏览器接口的环境中很常用。但是要注意,当使用门面模式的时候,要试着了解涉及的任何性能成本,并确认是否值得抽象。
源自:JavaScript设计模式(人民邮电出版社)——第十章,门面模式
参考:《JavaScript设计模式》 —— 9.9 Facade(外观)模式