5分钟读书笔记之 - 设计模式 - 门面模式

门面模式有俩个作用:

  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之后这种情况经常出现,那么你也许应该考虑用一个门面函数把这俩个函数组合起来。

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

5分钟读书笔记之 - 设计模式 - 门面模式

时间: 2024-10-06 01:48:28

5分钟读书笔记之 - 设计模式 - 门面模式的相关文章

5分钟读书笔记之 - 设计模式 - 单体模式

单体是一个用来划分命名空间,并将一批相关方法和属性组织在一起的对象,如果它可以被实例化,那么它只能被实例化一次. 单体模式,就是将代码组织为一个逻辑单元,这个逻辑单元中的代码可以通过单一的变量进行访问. 单体基本结构是这样: var Singleton = { attribute1:true, attribute2:10, method1:function(){}, method2:function(){} } 借助闭包实现单体: Namespace.Singleton = {} 定义之后立即执

5分钟读书笔记之 - 设计模式 - 组合模式

组合模式是一种专为创建Web上的动态用户界面而量身定制的模式,使用这种模式,可以用一条命令在对各对象上激发复杂的或递归的行为. 在组合对象的层次体系中有俩种类型对象:叶对象和组合对象.这是一个递归定义,但这正是组合模式如此有用的原因所在.一个组合对象由一些别的组合对象和叶对象组成,其中只有叶对象不再包含子对象,叶对象是组合对象中最基本的元素,也是各种操作的落实地点. 存在一批组织成某种层次体系的对象(具体的结构在开发期间可能无法得知) 希望这批对象或其中的一部分对象实施一个操作 表单验证实例:

5分钟读书笔记之 - 设计模式 - 工厂模式

一个类或者对象中,往往会包含别的对象.在创建这种对象的时候,你可能习惯于使用常规方式,即用 new 关键字和类构造函数. 这会导致相关的俩个类之间产生依赖. 工厂模式,就是消除这俩个类之间的依赖性的一种模式,它使用一种方法来决定究竟实例化那个具体的类. 简单工厂模式 假设你想开几个自行车商店,每个商店都有几种型号的自行车出售,可以用这样一个类来表示: var BicycleShop = function(){} BicycleShop.prototype = { sellBicycle:func

5分钟读书笔记之 - 设计模式 - 桥接模式

补充一点知识: 私有变量 在对象内部使用'var'关键字来声明,而且它只能被私有函数和特权方法访问.私有函数 在对象的构造函数里声明(或者是通过var functionName=function(){...}来定义),它能被特权函数调用(包括对象的构造函数)和私有函数调用.特权方法 通过this.methodName=function(){...}来声明而且可能被对象外部的代码调用.可以使用:this.特权函数() 方式来调用特权函数,使用 :私有函数()方式来调用私有函数.公共属性 通过thi

5分钟读书笔记之 - 设计模式 - 命令模式

本章研究的是一种封装方法调用的方式.命令模式与普通函数有所不同.它可以用来对方法调用进行参数化处理和传送,经过这样处理过的方法调用可以在任何需要的时候执行. 它也可以用来消除调用操作的对象和实现操作的对象之间的耦合.这为各种具体的类的更换带来了极大的灵活性.这种模式可以用在许多不同的场合,不过它在创建用户界面这一方面非常有用,特别是在需要不受限的取消操作的时候.它还可以用来替代回调函数,因为它能够提高在对象之间传递的操作的模块化程度. 命令的结构: 最简形式的命令对象是一个操作和用以调用这个操作

5分钟读书笔记之 - 设计模式 - 装饰者模式

本章讨论的是一种为对象增添特性的技术,它并不使用创建新子类这种手段. 装饰者模式可以透明地把对象包装在具有同样接口的另一对象之中,这样一来,你可以给一些方法添加一些行为,然后将方法调用传递给原始对象.相对于创建子类来说,使用装饰者模式对象是一种更灵活的选择. 装饰者可用于为对象增加功能.它可以用来替代大量子类. 考虑前面的自行车类,你现在可能提供一些配件供用户选择,装饰者模式要求我们只需要创建选件类,这些类与四种自行车类都要实现Bicycle接口,但是他们只被用作这些自行车类的包装类.在这个例子

5分钟读书笔记之 - 设计模式 - 适配器模式

适配器模式可以用来在现在接口和不兼容的类之间进行适配. 使用这种模式的对象又叫包装器,因为他们是在用一个新接口包装另一个对象. 在设计类的时候往往遇到有些接口不能与现有api一同使用的情况,借助于适配器,你可以不用直接修改这些类也能使用他们. 适配器的特点: 适配器可以被添加到现有代码中以协调俩个不同的接口.从表面上来看,适配器模式很像门面模式,他们都要对别的对象进行包装并改变其呈现的接口,二者之间的差别在于他们如何改变接口.门面元素展现的是一个简化接口,它并不提供额外的选择,而且有时是为了方便

5分钟读书笔记之 - 设计模式 - 享元模式

本章探讨另一种优化模式-享元模式,它最适合于解决因创建大量类似对象而累及性能的问题.这种模式在javascript中尤其有用,因为复杂的javascript代码很快就会用光浏览器的所有可用内存,通过把大量独立对象转化为少量共享对象,可以降低运行web应用程序所需的资源数量. 享元模式用于减少应用程序所需对象的数量.这是通过对对象的内部状态划分为内在数据和外在数据俩类实现的.内在数据是指类的内部方法所需要的信息,没有这种数据的话类就不能正常运转.外在数据则是可以从类身上剥离并存储在其外部的信息.我

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

行为型:Command(命令模式) 命令模式: 目的:其实一般设计模式就是为了解耦.也没什么特别的,命令模式实际上就是将命令的请求者和命令的执行者解耦. 白话:领导说了,让把这个月的项目计划压缩到三个礼拜完成,还说了:"不管你用什么办法".这句“不管你用什么办法”就是我们所说的解耦.我不需要关心你怎么去做,我只要你能实现我想达到的目的. 模式结构:一般包含下面几个部分. Client:客户 Invoker:命令触发者 Command:命令 ConcreteCommand:具体命令实现