门面模式有俩个作用:

  1. 简化类的接口
  2. 消除类与使用它的客户代码之间的耦合

在javascript中,门面模式常常是开发人员最亲密的朋友。它是几乎所有javascript库的核心原则,门面模式可以使库提供的工具更容易理解。使用这种模式,程序员可以间接地与一个子系统打交道,与直接访问子系统相比,这样做更不容易出错。

addEvent函数是一个基本的门面,你不用在每次为一个元素添加事件监听器的时候都得针对浏览器间的差异进行检查,有了这个便利,你可以把这个添加事件的底层细节抛在脑后,而把心思集中在如何构建自己的应用系统上。

用作便利方法的门面元素

门面模式给与开发人员的另一个好处表现在对函数的组合上。这些组合而得到的函数又叫便利函数。如下例子:

function a(){}
function b(){}
function e(){
a();
b();
}

你可能在想为什么一开始不把所有功能放到函数e中,答案是分离可以获得更多粒度控制和灵活性。组合a和b可能会对应用程序造成破坏或者产生意想不到的结果,如以DOM脚本编程中经常遇到的俩个普通事件方法为例:

  • event.stopPropagation()
  • event.preventDefault()

第一个stopPropagation功能是中止事件沿DOM树向上冒泡的传播过程

第二个方法preventDefault是阻止浏览器针对一个事件的默认行为。因为不同浏览器厂商为这俩个功能提供的接口略有差异,所以现在摆在我们面的就是一个门面模式实现便利方法的理想案例。

var DED = window.DED || {};
DED.util = {
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){
DED.util.stopPropagation(e);
DED.util.preventDefault(e);
}
}

尽管看起来很像,但是门面模式并不是适配器模式。适配器模式是一种包装器,用来对接口进行适配以便在不兼容的系统中使用它。而创建门面模式则是图个方便,它并不用于达到与需要特定接口的客户端系统打交道这个目的,而是用于提供一个简化的接口。

示例:设置HTML元素的样式:

不停的写getElementById并且为每一个元素设置同样的属性,看起来相当的无聊,门面模式就可以派上用场,我们采用一种逆向的工作方式,先写出使用方法,然后设计代码。

setStyle(['foo','bar','baz'],'color','red');

可以看出,要创建setStyle函数,这里传递给它的第一个参数是一个包含着三个ID值得数组。第二个参数是要设置的样式属性,而第三个参数则是该属性的值。下面这个函数就是一个门面元素,它可以满足我们的需要:

function setStyle(elements,prop,val){
for(var i=0,len=elements.length-1;i<len;++i){
document.getElementById(elements[i]).style[prop] = val;
}
}

我们可以设计一个更复杂的接口,把所有逻辑都组合在另一个门面元素中,以便一次函数调用就能处理所有这些问题。这个门面元素内部也要使用setStyle,但是客户代码对此一无所知。我们把它命名为setCSS:

setCSS(['foo'],{
position:'absolute',
top:'50px',
left:'20px'
});

setCSS的实现方式如下:

function setCSS(el,styles){
for (var prop in styles) {
if(!styles.hasOwnProperty(prop)){
continue;
}else{
setStyle(el,prop,styles[prop]);
}
}
}

设计一个事件工具:

前面曾经说过,在处理跨浏览器开发的问题时,最好创建一个门面模式,如果要设计一个大型库,那么最好把其中所有的工具元素聚拢在一起,这样更好用,访问起来也更简单。鉴于各种浏览器在事件处理方面表现出来的差异,开发一个事件工具很有必要。

我们先从一个基本的框架开始,这里要用到单体模式,它位于DED.util命名空间中,包含着我们要设计的各个静态方法:

DED.util.Event = {
// bulk goes here ...
}

接下来我们将着手解决开发人员在与事件打交道的时候都会遇到的一些常见的问题,比如怎么获得事件目标元素和事件对象。当然,我们也会利用前面用来处理事件传播和事件默认行为的代码。下面是粗略的框架结构:

DED.util.Event = {
getEvent:function(){},
getTarget:function(){},
stopPropagation:function(e){},
preventDefault:function(e){},
stopEvent:function(e){}
}

下面的代码对相关对象的能力和特性进行检查并加入一些代码分支,以图弥合浏览器之间的差异,其结果是创建了5个门面方法,这是一个更为一致的接口,有了它我们的工作就会变得更轻松。

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.util.preventDefault(e);
}
}

现在这个事件工具设计好了,可以和前面的addEvent函数结合使用

addEvent('example','click',function(e){
console.log(DED.util.Event.getTarget(e));
DED.util.Event.stopEvent(e);
})

实现门面模式的一般步骤:

函数名字应该仔细考虑,与他们用途要相称,对那些有几个函数组合而成的函数,一个简单的方法就是把相关函数的名称串成一个函数名,并采用camel大写规范。或者也可以使用thisFunctionAndThatFunction这种形式。

处理浏览器的API的不一致性属于另一种情况,此时要做的就是把分支代码放在新创建的门面函数中,辅以对象检查或者浏览器嗅探技术。

门面模式的适用场合:

判断是否应该使用门面模式的关键在于辨认那些反复成组出现的代码,如果函数b出现在函数a之后这种情况经常出现,那么你也许应该考虑用一个门面函数把这俩个函数组合起来。

使用门面模式的好处就是,编写一次组合代码,可以反复使用。避免与下层子系统的紧密耦合。

读书笔记之 - javascript 设计模式 - 门面模式的更多相关文章

  1. 读书笔记之 - javascript 设计模式 - 代理模式

    代理(proxy)是一个对象,它可以用来控制对另一对象的访问.它与另外那个对象实现了同样的接口,并且会把任何方法调用传递给那个对象.另外那个对象通常称为本体.代理可以代替本体被实例化,并使其可被远程访 ...

  2. 读书笔记之 - javascript 设计模式 - 命令模式

    本章研究的是一种封装方法调用的方式.命令模式与普通函数有所不同.它可以用来对方法调用进行参数化处理和传送,经过这样处理过的方法调用可以在任何需要的时候执行. 它也可以用来消除调用操作的对象和实现操作的 ...

  3. 读书笔记之 - javascript 设计模式 - 单体模式

    单体是一个用来划分命名空间,并将一批相关方法和属性组织在一起的对象,如果它可以被实例化,那么它只能被实例化一次. 单体模式,就是将代码组织为一个逻辑单元,这个逻辑单元中的代码可以通过单一的变量进行访问 ...

  4. 读书笔记之 - javascript 设计模式 - 组合模式

    组合模式是一种专为创建Web上的动态用户界面而量身定制的模式,使用这种模式,可以用一条命令在对各对象上激发复杂的或递归的行为. 在组合对象的层次体系中有俩种类型对象:叶对象和组合对象.这是一个递归定义 ...

  5. 读书笔记之 - javascript 设计模式 - 工厂模式

    一个类或者对象中,往往会包含别的对象.在创建这种对象的时候,你可能习惯于使用常规方式,即用 new 关键字和类构造函数. 这会导致相关的俩个类之间产生依赖. 工厂模式,就是消除这俩个类之间的依赖性的一 ...

  6. 读书笔记之 - javascript 设计模式 - 适配器模式

    适配器模式可以用来在现在接口和不兼容的类之间进行适配. 使用这种模式的对象又叫包装器,因为他们是在用一个新接口包装另一个对象. 在设计类的时候往往遇到有些接口不能与现有api一同使用的情况,借助于适配 ...

  7. JavaScript设计模式--门面模式

    外部与一个子系统的通信必须通过一个系统的一个门面对象进行,这就是门面模式. 门面模式具备如下两个角色: 1. 门面角色 客户端可以调用这个角色方法,此角色中有子系统的应用(知晓相关的(一个或多个)子系 ...

  8. 读书笔记之 - javascript 设计模式 - 享元模式

    本章探讨另一种优化模式-享元模式,它最适合于解决因创建大量类似对象而累及性能的问题.这种模式在javascript中尤其有用,因为复杂的javascript代码很快就会用光浏览器的所有可用内存,通过把 ...

  9. 读书笔记之 - javascript 设计模式 - 责任链模式

    责任链模式可以用来消除请求的发送者和接收者之间的耦合.这是通过实现一个由隐式地对请求进行处理的对象组成的链而做到的.链中的每个对象可以处理请求,也可以将其传给下一个对象. 责任链的结构: 责任链由多个 ...

随机推荐

  1. ACM1229_还是A+B(求A的第K位的数公式:A%((int)(pow(10,K)))

    #include<stdio.h> #include<math.h> int main() { int A,k,B,sum,c,d; while(scanf("%d% ...

  2. [zz] makefile中=和:=的区别

    转载自:http://www.cnblogs.com/wanqieddy/archive/2011/09/21/2184257.html 在Makefile中我们经常看到 = := ?= +=这几个赋 ...

  3. 5 approach to load UIView from Xib

    After the past few years I found that the only manageable way for creating/maintaining view (or any ...

  4. Centos6 编译安装局域网NTP服务器

    一.条件及环境: 1.CentOS:我用的是最新的CentOS 6.5 64位版,此教程应该也适用于RedHat 6及CentOS 6系列所有系统: 2.编译所需环境: 1 # yum -y inst ...

  5. Jackson中的那些坑

    不符合驼峰规范的变量 “驼峰命名法”请自行百度.简单的来说就是变量的第一个单词以小写字母开始其他单词首字母大写,或者全部单词首字母都大写,分别称为“小驼峰”和“大驼峰” 比如一个符合驼峰规范命名的实体 ...

  6. tail head命令

    显示1.txt的最后2行    tail -2 1.txt 显示1.txt的最后1行    tail -n 1 1.txt    tail -1 1.txt 显示1.txt的最后10行 tail 1. ...

  7. 问题-[Delphi]SendMessageTimeout调用后卡住点击任务栏还会出现窗体处理

    问题现象:在使用SendMessageTimeout函数后,5秒后WIN把进程挂在起.这时把程序最小化(原因就是不想让用户看到卡的界面),但点击任务栏按钮界面还原了,拦截消息失败(原因是挂起后消息都放 ...

  8. weblogic开发模式与生产模式介绍

    weblogic开发模式与生产模式介绍 开发模式:该模式启用自动部署 生产模式:该模式关闭自动部署 weblogic server 三种部署方法:自动部署.控制台部署.命令部署 自动部署:当其处于启用 ...

  9. 8-6-Exercise

    HDU 1003    Max Sum 题意:给出一串数字,求出其中某段连续的数字之和最大的值,同时要输出起点的位置和终点的位置~~~ 方法一: 用sum记录某一段和的值,maxx为目前为止最大的su ...

  10. SSH开源框架考试题

    一.选择题 1.不属于Action接口中定义的字符串常量的是____B___. A.SUCCESS                              B.FAILURE C.ERROR     ...