设计模式-适配器模式

670 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

适配器模式又称为包装器模式,将一个类的接口转化为用户需要的另外一个接口,主要是为了解决对象之间接口不兼容的问题,比如随着业务迭代升级出现了旧的接口与心的接口不兼容,这个时候不可能强制使用旧接口的用户去升级,而是在中间加一个适配器进行转换,让旧接口的使用者无感使用,保证了稳定性,在日常生活中适配器的案例随处可见,比如耳机插口不统一、充电接口不统一等,这个时候就需要一个适配器来解决问题

UML:

image-20211007135111668.png

接下看一下UML对应的代码实现:

class Target {
    constructor(type) {
        let result;

        switch (type) {
            case "adapter":
                result = new Adapter();
                break;
            default:
                result = null;
        }
        return result;
    }

    Request() {
            console.log('Target Request');
    }
}

class Adaptee {
    constructor() {
        console.log("Adaptee created");
    }

    SpecificRequest() {
        console.log("Adaptee request");
    }
}

class Adapter extends Adaptee {
    constructor() {
        super();
        console.log("Adapter created");
    }

    Request() {
        return this.SpecificRequest();
    }
}

function init_Adapter() {
    var f = new Target("adapter");
    f.Request();
}

init_Adapter();

应用场景

开发中常用的axios,其支持node端和浏览器端,那么在不同端调用axios需要进行不同的处理,而这些对于使用者而言都是无感的,我们在使用的时候都是使用同一套API直接干就完事了,不会在意内部具体做了些什么,这个时候就需要使用适配器来抹平不同端的差异,让使用者用着开心

接下来可以模拟简单实现这个过程

使用axios请求一个接口:

axios({
    url: "xxx",
    method: "GET",
})
    .then((res) => {
        console.log("success:", res);
    })
    .catch((err) => {
        console.log("fail:", err);
    });

接下来需要手动实现axios函数:

function axios(config) {
    let adaptor = getDefaultAdaptor();

    // 无论是node端 还是 浏览器端,在使用的时候都只是转入一个config配置对象,返回一个 Promise 对象
    return adaptor(config);
}

上文说到因为axios可以在浏览器端和node端使用,getDefaultAdaptor函数就是起到适配器的作用,根据不同的环境分别创建不同的adaptor

/**
 * 适配器
 */
function getDefaultAdaptor() {
    let adaptor;

    if (typeof XMLHttpRequest !== "undefined") {
        // 是浏览器环境
        adaptor = xhr;
    } else if (typeof process !== "undefined") {
        // node 环境
        adaptor = http;
    }

    return adaptor;
}

其中xhr和http为两个函数,用于创建具体的请求,至此适配器的使用已经完成,接下来就看看不同端是如何实现的接口请求

浏览器端:

/**
 * 浏览器环境
 */
function xhr(config) {
    return new Promise((resolve, reject) => {
        const req = new XMLHttpRequest();
        req.open(config.method, config.url, true);

        req.onreadystatechange = function () {
            if (req.readyState === 4) {
                if (req.status >= 200 && req.status < 300) {
                    resolve(req.responseText);
                } else {
                    reject("请求失败");
                }
            }
        };

        req.send();
    });
}

node端:

/**
 * node 环境
 */
function http(config) {
    const url = require("url");
    const http = require("http");
    // 将需要的参数 解析出来
    const { hostname, port, path } = url.parse(config.url);
    return new Promise((resolve, reject) => {
        const options = {
            hostname,
            port,
            path,
            method: config.method,
        };

        const req = http.request(options, function (response) {
            let chunks = [];

            response.on("data", (chunk) => {
                chunks.push(chunk);
            });

            response.on("end", () => {
                const res = Buffer.concat(chunks).toString();
                resolve(res);
            });
        });

        // 监听请求异常
        req.on("error", (err) => {
            reject(err);
        });

        // 请求发送完毕
        ren.end();
    });
}