焖面&适配器

外包达人
• 阅读 1980

门面模式

"焖面",有两个作用,一是简化类的接口;二是消除类与使用他的业务代码之间的耦合.他几乎是所有 JS 库的核心原则.通过建立一些便利方法可以让复杂系统变得更加简单易用,焖面模式可以使库提供的工具更加容易理解.

焖面可以简化错误记录或者跟踪页面视图统计数据这类这类常用,重复性的任务,通过添加一些方法(是对元有一些方法的组合利用),还可以让对象的功能更完善.
焖面可以简化复杂接口,可以在背后为你进行错误检查,清除不再需要的大对象,以及用一种简单方式展现对象功能.
这是一种组织性的模式,可以用来修改类和对象的接口,使其更易使用.

常见的焖面元素

快捷方式图标,是在引导用户至某个地方的接口,如果没有快捷方式,那些内部深层次的文件或者目录就不太容易查找.
基于 GUI 的 OS 就是计算机数据和功能的一个焖面.每次点击,拖动或者移动东西,实际上是在操作焖面,间接地执行一些背后的命令.
这里有个兼容各种浏览器的事件监听器例子(之前桥接也有个事件监听器例子,但是现在要涉及 addEvent()函数的定义:

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

JS 是一种事件驱动的语言,addEvent 函数是一个基本的焖面,每次为一个元素添加事件监听器时都得针对浏览器间的差异进行检查而烦恼.

为了尽量提高开发效率,简化常见任务,提供兼容每个浏览器各自内置 JS 函数实现的共同接口(这样就会方便许多),诞生了许许多多工具函数集(比如说 loadash),还有Prototype, jQuery, YUI, Vue, React, Angular 等等第三方 JS 库.

焖面的一个好处是对函数的组合--convenience function:

function a(x) {
    ...
}
function b(y) {
    ...
}
function ab(x, y) {
    a(x);
    b(y);
}

之所以依然保留前两个函数,是出于细粒度和灵活性的考虑.

看起来有点类似于桥接模式连接多个类...不过不能混淆,这里的焖面仅仅是对函数的组合,没有涉及到类.
用 DOM 脚本编程经常用到的两个普通事件方法为例:

event.stopPropagation()
event.preventDefault()

前者用来中断事件沿着 DOM 树向上冒泡传播,后者是阻止浏览器针对一个事件的默认行为.比如说防止浏览器链接点击跳转新页面,还可以用来阻止表单的提交.
不同浏览器为这两个功能提供的接口略有差异,所以:

var DED = window.DED || {};
DED.util = {
    stopPropagation: function (e) {
        if (ev.stopPropagation) {
            // W3 interface
            e.stopPropagation();
        }
        else {
            // IE's interface
            e.cancelBubble = true;
        }
    },
    preventDefault: function (e) {
        if (e.preventDefault) {
            // W3 interface
            e.preventDefault();
        }
        else {
            // IE's interface
            e.returnValue = false;
        }
    },

    // our convience method.
    stopEvent: function (e) {
        DED.util.stopPropagation(e);
        DED.util.preventDefault(e);
    }

}

焖面和适配器看起来很像,但是实际上,适配器是一种包装器,对接口进行适配以便在不兼容系统中使用它,而创建焖面元素主要是方便起见,提供一个简化的接口,并不适合于需要特定接口的系统.

设置 HTML 元素样式

现在要用原生 js一次设置几个元素的某个样式,很合理:

var ele1 = document.getElementById('foo');
ele1.style.color = '#f00';

var ele2 = document.getElementById('foo');
ele2.style.color = '#f00';

var ele3 = document.getElementById('foo');
ele3.style.color = '#f00';

不停地写 getElementById 并且为每个元素设置同样的属性和数值肯定就不对,这时候用上焖面,创建一个接口,简化成批设置元素样式的工作.现在我们逆向思维:先想象一下如何最方便地使用该方法,然后再编写方法源码本身:

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

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

设计一个事件工具

在处理浏览器的开发问题时,最好创建一些焖面函数,如果要设计一个大型库,最好把其中所有的工具元素拢在一起,这样更好用,访问起来也简单.鉴于各种浏览器在事件处理方面表现出来的大量差异,开发一个事件工具很有必要.
要用到单体模式,位于DED.Util 命名空间中,包含着各个静态方法,比如说,怎样获得事件目标元素和事件对象,还有如何处理事件传播和事件默认行为:

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

焖面模式的适用场合

判断是否应该应用焖面模式的关键在于辨认那些反复成组出现的代码,如果两个函数经常一块出现,那么或许可以可以考虑写一个组合式的焖面函数;
在核心工具代码中使用焖面函数的另一个目的是应对 js 内置函数在不同浏览器中的不同表现.这样做并不是因为不能直接使用这些 API,而是因为在处理浏览器差异问题时最好的解决办法是把这些差异抽取到焖面方法中
,比如说addEvent 函数可以提供一个更一致的接口.

编写一次组合代码,然后可以反复使用,并且提供了一个处理常见问题和任务的简化接口,也提供了较高层的功能,降低了对外部代码的依赖程度,为应用系统的开发增加了一些额外的灵活性.通过使用焖面模式,可以避免与下层子系统紧密耦合,可以对这个系统进行修改而不会影响到业务代码.

焖面模式经常会被滥用,相比一个庞杂的焖面函数,一个更简单的组成函数在粒度方面更有吸引力.

适配器

它用来在现有接口和不兼容的类之间进行适配.使用这种模式的对象叫做包装器 wrapper,他们是在用一个新的接口包装另一个对象.在设计类的时候往往会遇到有些接口不能与现有 API 一同使用的情况,借助于适配器,不用直接修改这些类也能使用它们.现在将考察这类场合,并探讨用适配器模连接对象的各种方式.

适配器特点

适配器可以被添加到现有代码中以协调两个不同的接口.如果现有代码的接口能很好地满足需要,那么可能没有必要使用适配器.但是如果现有接口对于手头的需求来说不够直观或者实用,那么可以使用适配器来提供一个更简洁或者更丰富的接口.

从表面上看,适配器模式很想焖面模式,他们都要对别的对象进行包装并且改变其呈现的接口,差别在于如何改变接口.焖面元素展现的是一个简化的接口,它并不提供额外的选择,而且有时候为了方便完成常见任务他还会做出一些假定;而适配器则要把一个接口转换另一个接口,他并不会滤除某些能力,也不会简化接口.

适配器可以被实现为不兼容方法调用之间的一个代码薄层.如果你有一个居右3个字符串参数的函数,但是客户系统拥有的是一个包含3个字符串元素的数组,那么就可以用一个适配器来衔接二者:

var clientObject = {
    string1: 'foo',
    string2: 'var',
    string3: 'baz'
};
function interfaceMedthod(str1, str2, str3) {
    ...
}

// in order to pass clientObject-param to interfaceMethod-function, we need 适配器:
function clentToInterfaceAdapter(srcObj) {
    interfaceMethod(srcObj.string1, srcObj.string2, srcObj.string3);
}

clentToInterfaceAdapter(clientObject);

clientToInterfaceAdapter 函数的作用就在于对 interfaceMethod 函数进行包装,并把传递给它的参数转换为后者需要的形式.

适配器适用场合

适用于客户系统期待接口与现有 API 提供的接口不兼容这种场合.它只能用来协调语法上的差异问题.适配器所适配的两个方法执行的应该是类似的任务,否则的话无法解决问题.

有助于避免大规模修改现有业务代码,其工作机制:用一个新的接口对现有类的接口进行包装,这样业务代码就无需大动干戈.

如果需要彻底重写代码,另外适配器也会引入一批不要支持的新工具;
如果现有 API 还未定形或者新接口还没有定形那么适配器不会一直管用;
所以说适配器有可能是一种不必要的开销,不必引入.

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
java之设计模式
看了设计模式,感觉自己很多不理解什么意思,通过看博客别人写的理解,总结了一下,方便查阅。一、设计模式六大原则1、单一职责原则:定义:应该有且只有一个原因引起类的变化。注意:这里的类不光指类,也适用于方法和接口,比如我们常说的一个方法实现一个功能。2、开放封闭原则:定义:类、模块、函数等
zdd小小菜鸟 zdd小小菜鸟
2年前
设计模式面试
设计模式面试1.单例设计模式使用设计模式为了代码复用,增加可维护性。设计模式的六大原则:开闭原则、里氏代换原则、依赖倒转原则、接口隔离原则、迪米特法则(最少知道原则)、合成/聚合复用原则Singleton(创建):保证一个类仅
陈占占 陈占占
3年前
PHP 利用confirm删除指定数据库的数据
完整的效果图方法一a标签href中的是你要删除记录html<ahref"PHPtest.php?name1"onclick"returnconfirm('是否要移除该小说?')"方法二下面这个方法是js代码,点击获取id,弹出提示框,确定是否删除,confirm好像可以返回true或者falsejavascriptfunctiond
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
3年前
CoreOS实践指南(二):架设CoreOS集群
CoreOS集群的架设比架设一个传统服务器集群更加容易。一方面因为CoreOS使用了Cloudinit自动化了集群信息的配置,另一方面则是受益于etcd分布式存储实现的消息分发和服务器自发现机制。这些便利性正是CoreOS系统设计充分为集群架构考虑带来的效率提升。安装CoreOSCoreOS的安装方法和传统Linux系统有
Wesley13 Wesley13
3年前
Java开发中的23种设计模式详解(转)
设计模式(DesignPatterns)                                 ——可复用面向对象软件的基础设计模式(Designpattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。毫无疑问,设计模式于己于他
Wesley13 Wesley13
3年前
00_设计模式之语言选择
设计模式之语言选择设计模式简介背景设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。设计模式(Designpattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的
Wesley13 Wesley13
3年前
Java适配器设计模式 的优缺点
1\.定义:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。2\.适配器模式的本质转换匹配,复用功能。3\.优点:更好的复用如果功能是已经有了的,只是接口不兼容,那么通过适配器模式就可以让这些功能得到更好的复用。更好的可扩展在实现适配器功能的时候,可以调用自己
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(