javascript设计模式:适配器模式

适配器模式(Adapter Pattern)又称包装器模式,将一个类(对象)的接口(方法、属性)转化为用户需要的另一个接口,解决类(对象)之间接口不兼容的问题。

主要功能是进行转换匹配,目的是复用已有的功能,而不是来实现新的接口。也就是说,访问者需要的功能应该是已经实现好了的,不需要适配器模式来实现,适配器模式主要是负责把不兼容的接口转换成访问者期望的格式而已。

一、适配器模式生活实例

现实生活中我们会遇到形形色色的适配器,最常见的就是转接头了,比如不同规格电源接口的转接头、iPhone 手机的 3.5 毫米耳机插口转接头、DP/miniDP/HDMI/DVI/VGA 等视频转接头、电脑、手机、ipad 的电源适配器,都是属于适配器的范畴。

在类似场景中,这些例子有以下特点:

  1. 旧有接口格式已经不满足现在的需要;
  2. 通过增加适配器来更好地使用旧有接口;

二、适配器模式的实现

电源适配器的例子,一开始我们使用的中国插头标准:

但是我们出国旅游了,到了日本,需要增加一个日本插头到中国插头的电源适配器,来将我们原来的电源线用起来:

 

访问者需要目标对象的某个功能,但是这个对象的接口不是自己期望的,那么通过适配器模式对现有对象的接口进行包装,来获得自己需要的接口格式。

var chinaPlug = {
    name: 'china plug',
    chinaPlug() {
        console.log(this.name);
    }
}

var japanPlug = {
    name: 'japan plug',
    japanPlug() {
        console.log(this.name);
    }
}

function japanPlugAdapter(plug) {
    return {
        // 改变chinaPlug调用接口,不影响之前的调用接口
        chinaPlug() {
            return plug.japanPlug()
        }
    }
}
chinaPlug.chinaPlug()
japanPlugAdapter(japanPlug).chinaPlug()

三、适配器模式在实战中的应用

  • jQuery.ajax 适配 Axios

有的使用 jQuery 的老项目使用 $.ajax 来发送请求,现在的新项目一般使用 Axios,那么现在有个老项目的代码中全是 $.ajax,如果你挨个修改,那么 bug 可能就跟地鼠一样到处冒出来让你焦头烂额,这时可以采用适配器模式来将老的使用形式适配到新的技术栈上。

/* 适配器 */
function ajax2AxiosAdapter(ajaxOptions) {
    return axios({
        url: ajaxOptions.url,
        method: ajaxOptions.type,
        responseType: ajaxOptions.dataType,
        data: ajaxOptions.data
    })
      .then(ajaxOptions.success)
      .catch(ajaxOptions.error)
}

/* 经过适配器包装 */
$.ajax = function(options) {
    return ajax2AxiosAdapter(options)
}

$.ajax({
    url: '/demo-url',
    type: 'POST',
    dataType: 'json',
    data: {
        name: '张三',
        id: '2345'
    },
    success: function(data) {
        console.log('访问成功!')
    },
    error: function(err) {
        console.err('访问失败~')
    }
})
  • 业务数据适配

增加适配器后,就可以将原先状态的树形结构转化为所需的结构,而并不改动原先的数据,也不对原来使用旧数据结构的代码有所影响。

/* 树形结构平铺 */
function treeDataAdapter(treeData, lastArrayData = []) {
    treeData.forEach(item => {
        if (item.children) {
            treeDataAdapter(item.children, lastArrayData)
        }
        const { name, place } = item
        lastArrayData.push({ name, place })
    })
    return lastArrayData
}

// 返回平铺的组织结构
console.log("newData:", treeDataAdapter(oldTreeData))
console.log("oldData:", oldTreeData);
  • Vue 计算属性

旧有 data 中的数据不满足当前的要求,通过计算属性的规则来适配成我们需要的格式,对原有数据并没有改变,只改变了原有数据的表现形式。

<template>
    <div id="example">
        <p>Original message: "{{ message }}"</p>  <!-- Hello -->
        <p>Computed reversed message: "{{ reversedMessage }}"</p>  <!-- olleH -->
    </div>
</template>

<script type='text/javascript'>
    export default {
        name: 'demo',
        data() {
            return {
                message: 'Hello'
            }
        },
        computed: {
            reversedMessage: function() {
                return this.message.split('').reverse().join('')
            }
        }
    }
</script>

四、适配器模式的优缺点

适配器模式的优点:

  1. 已有的功能如果只是接口不兼容,使用适配器适配已有功能,可以使原有逻辑得到更好的复用,有助于避免大规模改写现有代码;
  2. 可扩展性良好,在实现适配器功能的时候,可以调用自己开发的功能,从而方便地扩展系统的功能;
  3. 灵活性好,因为适配器并没有对原有对象的功能有所影响,如果不想使用适配器了,那么直接删掉即可,不会对使用原有对象的代码有影响;

适配器模式的缺点:会让系统变得零乱,明明调用 A,却被适配到了 B,如果系统中这样的情况很多,那么对可阅读性不太友好。如果没必要使用适配器模式的话,可以考虑重构,如果使用的话,可以考虑尽量把文档完善。

 

posted @ 2020-06-06 08:47  Nyan  阅读(388)  评论(0编辑  收藏  举报