JavaScript设计模式:Facade外观模式的原理与实战应用

作者:函数茶馆时间:2026-10-30 06:49:36浏览:446

本文深入解析JavaScript中的Facade(外观)设计模式。通过对比适配器模式,阐明其提供高层简化接口的核心目的。结合事件监听、DOM样式设置等具体代码示例,展示如何利用外观模式隐藏复杂实现细节、降低模块耦合度,并讨论其在提升代码可维护性与避免过度封装之间的权衡。

外观模式概念解析

概念

外观模式(Facade Pattern),又称门面模式,是一种结构相对简单但应用广泛的设计模式。它通过提供一个高层接口,简化了客户端与子系统之间的交互,使得调用过程更加直观和便捷。

需要明确的是,外观模式与适配器模式有着本质区别。适配器模式主要作为一种包装器,用于将不兼容的接口转换为客户端期望的特定接口,以解决兼容性问题。而外观模式的核心目的在于简化调用流程,它并不旨在改变接口以适配特定系统,而是致力于提供一个更简洁、易用的统一接口。

JavaScript代码示例

JavaScript代码示例

用一段再简单不过的代码来表示


var getName = function(){
 return ''svenzeng"
}
var getSex = function(){
  return 'man'
}

假设你需要分别调用 getName 和 getSex 函数来获取用户信息。此时,可以创建一个更高层级的接口 getUserInfo,由它内部调用上述两个函数,从而为调用者提供统一的访问点。


var getUserInfo = function(){
 var info = a() + b();
 return info;
}

也许你会问,为什么一开始不把 getName 和 getSex 的代码合并在一起,比如直接写成一个函数?


var getNameAndSex = function(){
 return 'svenzeng" + "man";
}

答案显而易见。这就好比食堂的炒菜师傅,不会因为顾客同时点了烧鸭和白菜,就将两者混在一个锅里炒制。相反,师傅更倾向于提供一份“烧鸭饭套餐”。在程序设计中,我们需要保证函数或对象保持在合理的粒度范围内,因为并非所有功能都适合捆绑在一起。此外,外观模式还能对用户隐藏真正的实现细节,用户只需关注最高层的接口。例如在烧鸭饭的例子中,用户无需关心师傅是先做烧鸭还是先炒白菜,更无需知晓鸭子来源何处。最后,我们来看一个大家熟悉的外观模式应用案例。


var stopEvent = function( e ){  //同时阻止事件默认行为和冒泡
 e.stopPropagation();
 e.preventDefault();
}

我知道外观模式的概念相对容易掌握,甚至可能不需要JavaScript代码示例就能理解,但总有一部分开发者更倾向于通过代码来加深印象,认为这样更具说服力。毕竟,没有代码示例的JavaScript文章往往缺乏可信度。我们从最简单的事件监听器处理开始。众所周知,添加事件监听器并非易事,除非你只希望代码在少数几个浏览器上运行。为了确保代码在不同浏览器中正常运行,开发者不得不测试多种方法。在这个示例中,我们将特性检测封装到该方法中:


function addEvent(element, type, func) {
  if (window.addEventListener) {
    element.addEventListener(type, func, false);
  }
  else if (window.attachEvent) {
    element.attachEvent('on'+type, func);
  }
  else {
    element['on'+type] = func;
  }
}

简单吧!我真希望我可以不用写那些不必要的代码,让它们越简单越好,但是如果真是这样就没什么意思了,你也不会想读下去了,对吧?所以我不这么认为,我想我要给你看点更复杂的东西。我只是想说,你的代码原本看起来会有些像下面这样:


var foo = document.getElementById('foo');
  foo.style.color = 'red';
  foo.style.width = '150px';

var bar = document.getElementById('bar');
  bar.style.color = 'red';
  bar.style.width = '150px';

var baz = document.getElementById('baz');
  baz.style.color = 'red';
  baz.style.width = '150px';

太蹩脚了!你对每个元素做了一模一样的事!我认为我们可以让它变得更简单点:


function setStyle(elements, property, value) {
  for (var i=0, length = elements.length; i < length; i++) {
    document.getElementById(elements[i]).style[property] = value;
  }
}

// 现在你可以这么写:
setStyle(['foo', 'bar', 'baz'], 'color', 'red');
setStyle(['foo', 'bar', 'baz'], 'width', '150px');

是不是觉得咱们NB坏了?你快算了吧!咱们可是JavaScript程序员呀!能不能用点脑子,来点真格的。也许我们可以只调用一次就能设置所有的样式。看下这个:


function setStyles(elements, styles) {
  for (var i=0, length = elements.length; i < length; i++) {
    var element = document.getElementById(elements[i]);

    for (var property in styles) {
      element.style[property] = styles[property];
    }
  }
}

//现在你只要这样写:
setStyles(['foo', 'bar', 'baz'], {
  color: 'red',
  width: '150px'
});

如果我们有很多元素想设置相同的样式,那这段代码真是为我们节省了不少时间。

外观模式的利弊分析

外观模式之利: 使用外观模式的目的就是要让程序员过的更轻松一些,编写一次组合代码,然后就可以反复使用它,这有助于节省时间和精力。给一些复杂的问题提供一个简化接口。

外观方法方便了开发人员,斌共提供过了比较高层的功能,降低对外部代码的依赖程度,为应用系统的开发增加了一些额外的灵活性。通过使用外观模式,可以避免与下层子系统紧密耦合。这样就可以对这个系统进行修改而不会影响到客户代码。

外观模式之弊: 有时候外观元素也会带来一些不必要的额外负担。在实施一些套路之前应该认真掂量一下其实用性。有时相比一个庞杂的外观函数,其组成函数在力度方面更有吸引力。这是因为外观函数可能常常会执行一些你并不需要的任务。

对于简单的个人网站或少量营销网页来说,仅为工具提示和弹出式窗口这样一点增强行为就导入这个Javascript库可能并不明智。此时考虑只使用少许简单的外观元素而不是一个满是这类东西的库。

外观函数为执行各种复杂任务提供了一个简单的接口,它们使代码更容易维护和理解。它们还能弱化子系统和客户代码的耦合。把经常相伴出现的常用函数组合在一起。这个模式在DOM脚本编程这种需要面对葛洪不一致的浏览器接口的环境中很常用。

作者最新文章