门面模式有俩个作用:

  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. Ubuntu---2

    1.Ubuntu学习笔记之:安装中文语言包 http://askubuntu.com/questions/59356/how-do-i-get-chinese-input-to-work

  2. Yii PHP 框架分析(四)

    作者:wdy http://hi.baidu.com/delphiss/blog/item/c15b314f05f9dfc0d0c86a26.html Yii应用的入口脚本最后一句启动了WebAppl ...

  3. Creating InetAddress object in Java

    I am trying to convert an address specified by an IP number or a name, both in String (i.e. localhos ...

  4. liunx shell数字相加

    #!/bin/bash num1= num2= num3= #echo $($num1+$num2+$num3)#错误写法 echo $[$num1+$num2+$num3] echo $(($num ...

  5. db2 identity列重置,reset/restart

    db2中可以对表中的某一个列创建identity列,用于自动填充值,某些情况下(比如删除数据后,需要从最小值开始,并不重复,那可以对标识列进行reset操作) 语法: ALTER TABLE < ...

  6. SQL 主键和外键约束

    SQL的主键和外键的作用: 外键取值规则:空值或参照的主键值. (1)插入非空值时,如果主键表中没有这个值,则不能插入. (2)更新时,不能改为主键表中没有的值. (3)删除主键表记录时,你可以在建外 ...

  7. Ajax交互demo1

    一.概念 Ajax异步请求刷新. 浏览器在用户不知道的情况下,偷偷地跟服务器交互,然后返回数据给浏览器显示. 异步过程:当HTTP请求发送后,通过Ajax技术使用的XMLHttpRequest对象来发 ...

  8. OGG-01224 Bad file number

    今天在看OGG的日志时.发现例如以下OGG-01224 Bad file number错误.查阅资料才知道port不可用,看了一下mgr的參数,发现是设置的DYNAMICPORTLIST 动态port ...

  9. Android时间互换代码

    收藏备用. http://www.oschina.net/code/snippet_575610_22694 /** * 获取现在时间 * * @return 返回时间类型 yyyy-MM-dd HH ...

  10. Windows环境下tomcat配置日志输出

    在Linux系统中,可以通过tail  -f  catalina.out 来跟踪Tomcat 和相关应用运行的情况. 在windows下,catalina日志与Linux记录的内容有很大区别,大多信息 ...