从 axios 源码学习设计模式

这篇具有很好参考价值的文章主要介绍了从 axios 源码学习设计模式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


本篇文章并不会从0开始对 axios 进行分析,而是对 axios 的一些关键地方进行总结
另外,本篇大部分内容都是对一些现有文章的总结
参考文章:
Axios基本原理深度解析

一、源码分析

1.1 axios 为什么可以多种方式调用

首先,我们在使用Axios的时候,会有很多种用法。是怎么实现的呢?
axios都能怎么调用

// 第一种方法
axios(config)
// 第二种
axios('example/url'[, config])
// 第三种
axios.request(config)
// 第四种
axios.get(url[, config])//'delete', 'get', 'head', 'options'请求方法一样的调用方式
// 第五种
axios.post(url[, data[, config]])// 'post', 'put', 'patch'请求方法永阳的调用方式
// 第六种
axios.all([axios1, axios2, axios3]).then(axios.spread(function (axios1response, axios2response, axios3response) {
    // 三个请求现在都执行完成
  }));
// 还可以通过axios.create方法建立自定义全局默认配置的Axios实例
axios.create(config)

总结有六种调用方式
相关代码

function createInstance(defaultConfig) {
  // 建立Axios对象
  var context = new Axios(defaultConfig);

  // Axios作者的目的是提供一个对外可用的方法。
  // 并且方法中需要用到Axios对象中的config属性和拦截器。
  // 所以要把axios原型上的方法单独拿出来,绑定context这个axios实例。
  // instance方法就是后面导出的axios实例,
  // 所以到这里位置 第一种调用方法 axios(config) 就实现了
  // 在request方法的内部,有对传入参数类型的判断,如果传入第一参数为字符串,则认为是url字符串,并且把url参数添加到第二个参数config中
  // 所以就实现了第二种调用方法axios('example/url'[, config])
  var instance = bind(Axios.prototype.request, context);

  // 这里把Axios原型上的方法和属性,扩展到instance方法上,
  // 并制定了原型上方法的this为context(上面定义axios对象)
  // Axios上有request方法,这里绑定了this为context
  // 所有第三种调用方法 axios.request(config) 就实现了
  // Axios原型中其实定义了get,post,option等等方法,
  // 所以第四种axios.get(url[, config])和第五种axios.post(url[, data[, config]])方法就实现了
  utils.extend(instance, Axios.prototype, context);

  // 这里把上面建立axios对象(context)中自有的属性方法,扩展到了instance中
  // 这样instance就有了defaults、interceptors 属性,就可以添加拦截器,操作defaultConfig了
  utils.extend(instance, context);

  return instance;
}

// 调用createInstance方法,建立了Axios实例
var axios = createInstance(defaults);

// 这里也调用上面的createInstance方法,同样建立了Axios实例,
// 只不过,这里配置了自己的config作为全局默认的config
// 所以这里实现了,通过axios.create方法建立自定义默认配置的Axios实例
axios.create = function create(instanceConfig) {
  return createInstance(mergeConfig(axios.defaults, instanceConfig));
};

// 这里添加了all方法,其实就是promise的all方法,
// 这就是第六种调用方法,并发请求的实现原理
axios.all = function all(promises) {
  return Promise.all(promises);
};
// spread方法就是把用数组作为一个参数,变成数组的每一项为一个参数。就是为了用着方便。
axios.spread = require('./helpers/spread');

module.exports = axios;// 对外导出实例

上面注释中提到的第二种调用方法axios(‘example/url’[, config])的实现是request内部做了判断所以才能那么用。
第四种axios.get(url[, config])和第五种axios.post(url[, data[, config]])方法的实现是因为Axios原型中定义了相应的方法,所以才得以实现。
关于 Axios:

// Axios构造函数,定义l额defaults属性和interceptors属性
function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(), //拦截器,后面会讲到
    response: new InterceptorManager()
  };
}

Axios.prototype.request = function request(config) {
  // 这里对传入参数类型的判断,如果传入第一参数为字符串,
  // 则认为字符串是url,并且把url参数添加到第二个参数config中
  // 所以就实现了第二种调用方法axios('example/url'[, config])
  if (typeof config === 'string') {
    config = arguments[1] || {};
    config.url = arguments[0];
  } else {
    config = config || {};
  }

  // ...省略了一些代码
  }

// 这里对Axios原型扩展了'delete', 'get', 'head', 'options'方法,
// 其实都是调用了request方法
// 结合上面lib / axios.js 代码中把原型中方法扩展到了instance上
// 所以第四种方法axios.get(url[, config])就实现了
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  Axios.prototype[method] = function(url, config) {
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url
    }));
  };
});

// 这里对Axios原型扩展了'post', 'put', 'patch'方法,
// 其实都是调用了request方法
// 结合上面lib / axios.js 代码中把原型中方法扩展到了instance上
// 所以第五种方法axios.post(url[, data[, config]])就实现了
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  Axios.prototype[method] = function(url, data, config) {
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});

1.2 拦截器实现

首先是如何使用的?
请求拦截器是先加的后执行,响应是先加的先执行

// 添加请求拦截器
const myRequestInterceptor = axios.interceptors.request.use(config => {
    // 在发送http请求之前对config做点什么
    return config; // 必须返回config否则后续http请求无法获取到config
}, error => {
    // 错误处理代码写在这里
    
    // 但是,为什么返回Promise.reject(error)
    // 这和promise的机制有关
    // 如果直接抛出error就相当于抛出一个对象,
    // 这就会运行下一级promise的fulfilled方法。并且参数是error,这个fulfilled参数应该是config才对
    // 或者运行到dispatchRequest,参数error,但dispatchRequest参数也要是config才对
    return Promise.reject(error);
});

// 添加响应拦截器
axios.interceptors.response.use(response => {
  // 对响应数据处理代码写这里
  return response; // 有且必须有一个response对象被返回
}, error => {
  // 对响应错误处理代码写这里
  
  // 同理请求拦截器,这需要返回Promise.reject(error);
  return Promise.reject(error);
});

// 移除某次拦截器
axios.interceptors.request.eject(myRequestInterceptor);

注册

axios有interceptor属性,里面有两个属性request和response,都是InterceptorManager对象,目的是存储注册的拦截器。

function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}

那么InterceptorManager都做了什么呢,

// 构造函数,在对象中定义handlers用来存储拦截器
function InterceptorManager() {
  this.handlers = [];
}

/**
 * 注册拦截器,并存储在handlers中
 * 参数fulfilled,用来拦截器处理数据的函数
 * 参数rejected,用来处理错误用的
 * 为什么这么设计,因为拦截器要通过Promise处理
 * 返回本条拦截器在数组handlers中的索引位置,以便提供给删除拦截器用
 */
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected
  });
  return this.handlers.length - 1;
};

/**
 * 通过注册时候返回的拦截器索引来删除拦截器
 */
InterceptorManager.prototype.eject = function eject(id) {
  if (this.handlers[id]) {
    this.handlers[id] = null;
  }
};
/**
 * 遍历拦截器的方法
 */
InterceptorManager.prototype.forEach = function forEach(fn) {
  utils.forEach(this.handlers, function forEachHandler(h) {
    if (h !== null) {
      fn(h);
    }
  });
};

注册完成了,那么怎么发挥作用呢,那就要看看request方法了。

使用:promise链式调用

var dispatchRequest = require('./dispatchRequest');

Axios.prototype.request = function request(config) {
  // 刚进入方法,首先要处理config这里存放着http请求的必要配置
  if (typeof config === 'string') {
    config = arguments[1] || {};
    config.url = arguments[0];
  } else {
    config = config || {};
  }

  config = mergeConfig(this.defaults, config);

  // Set config.method
  if (config.method) {
    config.method = config.method.toLowerCase();
  } else if (this.defaults.method) {
    config.method = this.defaults.method.toLowerCase();
  } else {
    config.method = 'get';
  }
  // 到这里处理完用于http请求的配置数据config
  
  // 先定义个数组,先放入一个dispatchRequest和undefined,
  // dispatchRequest前面的项目目录介绍里提到了,用来申请http请求用的
  // 为什么先建立个数组呢,作者的目的就是想先把拦截器和http请求先排好序
  // 然后再建立promise调用链,就可以一步一步的按顺序进行了
  // 入果没有理解,建议先深入研究一下Promise链式调用
  // 为什么先放dispatchRequest,又放个undefined呢
  // 可以先看一下下面怎么向chain插入拦截器的
  // 拦截器被插入到数组,并且一次向数组插入两个方法,interceptor.fulfilled, interceptor.rejected
  // 再看看后面建立promise链式调用的时候,分别用在了then的两个参数,是从数组中一起取两个的
  // 所以为了保证拦截器两个方法配对正确所以先插入[dispatchRequest, undefined]
  // 之所以用undefined,因为这里没法处理错综复杂而且多变的错误。而且这里也只能用来处理请求拦截器的错误。所以没有必要。
  // 所以用undefined,把错误抛到下面的promise,由用户定义处理方法。
  var chain = [dispatchRequest, undefined];

  // 先初始一个promise,value是config,
  // 提供给下面的promise用,也就是提供给请求拦截器用
  var promise = Promise.resolve(config);

  // 向chain数组插入请求拦截器。一对一对的插入
  // 注意这里是从前插入请求拦截器的
  // 所以用的时候,先注册的请求拦截器是后执行的,这点要注意
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });

  // 向chain数组插入相应拦截器。一对一对的插入
  // 相应拦截器是从后插入的
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });
  // 到此,把拦截器和http请求拍好顺序了
  // 下面就利用这个循序建立一个promise链
  // promise链让拦截器和http请求按照顺序执行了,执行顺序是:
  // 请求拦截器->http请求->相应拦截器
  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }

  return promise;
};

到这里,已经建立了一个promise链。先执行请求拦截器,按照需求修改config。然后执行http请求。请求结果,传到相应拦截器处理。最后抛出链调用最后的promise,我们用这个promise就能得到这一串处理的最终结果了。
从 axios 源码学习设计模式,JavaScript设计模式,学习,设计模式,javascript
从 axios 源码学习设计模式,JavaScript设计模式,学习,设计模式,javascript

二、从 axios 看设计模式

axios 的精髓在哪

通过简单学习 axios 源码,就会发现,它的源码并不难,比 vue 要简单很多。 axios 的精髓在于他已经迭代了40个版本,
npm 的 version 规则是首个版本号变化表示 api 不向下兼容,现在 axios 的主版本号已经从 0.x 迭代到了 1.x, 按理说应该和 vue 一样,是有 重大变更的。 而 axios 增加了这么多功能。却始终保持没有 api 明显变化。主要原因是 axios 内部使用了多种设计模式和架构模式。
比如 适配器,桥接,代理,抽象工厂,微内核设计,还有一些设计原则,axios里面都用的非常好。
所以,当我们在用任何版本的 axios 除了一些 bug 以外,没有什么兼容问题。

2.1 抽象工厂

工厂模式将对象的创建和实现分离(尤其是写库的时候,经常需要把创建和实现进行分离)。
使代码具备良好的封装性,可扩展性(符合开放封闭原则),高解耦性(符合最少知识原则(说白了,就是让你能够傻瓜式应用))。

为什么工厂模式返回的不是axios实例,而是axios.request?
因为我们要进行封装,把实例给用户,体现我们工厂模式的优点。假如我们返回axios实例,就造成了无法传config了,这就很糟糕,所以我们返回了方法,就能传参数了。就能同时满足axios({})和axios.request({})
妙啊

import Axios from './core/Axios';  // 引入Axios,是放axios的核心代码的
import { extend } from './helper/utils'; // 混入方法

// 工厂
function createInstance() {
  const axios = new Axios();
  const req = axios.request.bind(axios); //这里与源码有出入,但是意思一直,如果追求与源码的一致性,可以回头看看上面的源码分析部分
  // req继承axios的属性和方法
  // Object.getOwnPropertyNames(Axios.prototype)读取对象属性,可以读取不可不可遍历的属性
  extend(req, Object.getOwnPropertyNames(Axios.prototype), axios);
  extend(req, Object.getOwnPropertyNames(axios), axios);
  return req;
}

const axios = createInstance(); // 创建实例的工厂
export default axios;

axios.create – 创建新实例的工厂

axios.create给用户提供了自定义配置的接口,通过调用mergeConfig()来合并用户配置和默认配置。从而我们可以得到一个自定义的instance

axios.create = function create(instanceConfig) {
  return createInstance(mergeConfig(axios.defaults, instanceConfig));
};

2.2 微内核设计

Axios 是一个基于 Promise 的 HTTP 客户端库,它使用了微内核设计。微内核是一种软件设计模式,将核心功能与可选功能分离,通过插件机制实现灵活扩展。
在 Axios 的源码中,它的微内核设计可以总结为以下几个主要的组件:

  1. 核心库:核心库提供了基本的请求和响应处理逻辑,包括创建 XMLHttpRequest 对象、设置请求配置、发送请求、处理响应等。它是 Axios 的核心部分,负责处理最基本的 HTTP 请求和响应。
  2. 拦截器:拦截器是一种插件机制,可以在请求和响应的过程中插入自定义的逻辑。Axios 通过 request 拦截器和 response 拦截器来实现请求和响应的拦截和修改。拦截器可以用于添加公共请求头、请求参数的处理、错误的全局处理等。
  3. 转换器:转换器允许你在发送请求之前或接收到响应之后对数据进行自定义转换。Axios 提供了请求和响应数据的转换器接口,可以通过设置 transformRequest 和 transformResponse 来定制数据的格式化和解析。
  4. 取消操作:Axios 允许你通过创建一个 CancelToken 实例来取消请求。取消请求可以通过 cancel 方法来触发,调用 cancel 方法后,Axios 将会中止该请求并抛出一个 Cancel 类型的错误。
  5. 错误处理:Axios 对错误的处理进行了统一的封装。它使用 Promise 的错误处理机制,当请求或响应发生错误时,会通过 Promise 的 reject 来返回错误信息。此外,Axios 也提供了全局的错误处理机制,你可以通过配置 onError 来捕获并处理全局的错误。
  6. 扩展机制:Axios 支持通过自定义适配器、拦截器、转换器等机制来扩展其功能。通过这些机制,你可以添加自定义的逻辑、处理非标准的返回数据类型、修改请求配置等。这使得 Axios 的功能非常灵活和可扩展。
    以上是 Axios 中微内核设计的主要组件和机制。这种设计使得 Axios 的核心库非常精简和高效,同时可以根据需求灵活地扩展和定制功能。

首先 core 提供核心功能,然后各种功能:拦截器,错误处理,取消操作,转换器 都是重开一个文件夹,然后在 core 中进行引用

axios 的 目录:

├── /dist/                     # 项目输出目录
├── /lib/                      # 项目源码目录
│ ├── /cancel/                 # 定义取消功能
│ ├── /core/                   # 一些核心功能
│ │ ├── Axios.js               # axios的核心主类
│ │ ├── dispatchRequest.js     # 用来调用http请求适配器方法发送请求
│ │ ├── InterceptorManager.js  # 拦截器构造函数
│ │ └── settle.js              # 根据http响应状态,改变Promise的状态
│ ├── /helpers/                # 一些辅助方法
│ ├── /adapters/               # 定义请求的适配器 xhr、http
│ │ ├── http.js                # 实现http适配器
│ │ └── xhr.js                 # 实现xhr适配器
│ ├── axios.js                 # 对外暴露接口
│ ├── defaults.js              # 默认配置 
│ └── utils.js                 # 公用工具
├── package.json               # 项目信息
├── index.d.ts                 # 配置TypeScript的声明文件
└── index.js                   # 入口文件

2.3 适配器思想

axios 是 适配浏览器和node,兼容性,对外统一接口
axios在浏览器端使用XMLHttpRequest对象发送ajax请求;在node环境使用http对象发送ajax请求。

var defaults.adapter = getDefaultAdapter();
function getDefaultAdapter () {
	var adapter;
    if (typeof XMLHttpRequest !== 'undefined') {
    	// 浏览器环境
        adapter = require('./adapter/xhr');
    } else if (typeof process !== 'undefined') {
    	// node环境
        adapter = require('./adapter/http');
    }
   return adapter;
}

2.4 责任链模式

一个链条,上面有很多职责。
他的好处是链条上的各个职责,只需要关心自己的事情就行了,不需要知道自己的上一步是什么,下一步是什么,跟上下的职责都不耦合,这样当上下职责变化了,自己也不受影响,往链条上添加或者减少职责也非常方便。

Axios的拦截器有请求拦截器和响应拦截器,执行的顺序是请求拦截器 -> 发起请求 -> 响应拦截器,这其实就是一个链条上串起了三个职责。

// 先从用法入手,一般我们添加拦截器是这样写的 
// instance.interceptors.request.use(fulfilled, rejected)
// 根据这个用法我们先写一个Axios类。
function Axios() {
  // 实例上有个interceptors对象,里面有request和response两个属性
  // 这两个属性都是InterceptorManager的实例
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}

// 然后是实现InterceptorManager类
function InterceptorManager() {
  // 实例上有一个数组,存储拦截器方法
  this.handlers = [];
}

// InterceptorManager有一个实例方法use
InterceptorManager.prototype.use = function(fulfilled, rejected) {
  // 这个方法很简单,把传入的回调放到handlers里面就行
  this.handlers.push({
    fulfilled,
    rejected
  })
}

这些拦截器方法都是什么时候执行呢?当然是我们调用instance.request的时候,调用instance.request的时候真正执行的就是请求拦截器 -> 发起请求 -> 响应拦截器链条,所以我们还需要来实现下Axios.prototype.request:

Axios.prototype.request = function(config) {
  // chain里面存的就是我们要执行的方法链条
  // dispatchRequest是发起网络请求的方法,本文主要讲设计模式,这个方法就不实现了
  // chain里面先把发起网络请求的方法放进去,他的位置应该在chain的中间
  const chain = [dispatchRequest, undefined];
  
  // chain前面是请求拦截器的方法,从request.handlers里面取出来放进去
  this.interceptors.request.handlers.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });
  
  // chain后面是响应拦截器的方法,从response.handlers里面取出来放进去
  this.interceptors.response.handlers.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });
  
  // 经过上述代码的组织,chain这时候是这样的:
  // [request.fulfilled, request.rejected, dispatchRequest, undefined, response.fulfilled,  
  // response.rejected]
  // 这其实已经按照请求拦截器 -> 发起请求 -> 响应拦截器的顺序排好了,拿来执行就行
  
  let promise = Promise.resolve(config);   // 先来个空的promise,好开启then
  while (chain.length) {
    // 用promise.then进行链式调用
    promise = promise.then(chain.shift(), chain.shift());
  }

  return promise;
}

注意,请求拦截器用了 unshift 方法,响应拦截器用了 push 方法
是因为:请求拦截器是先加的后执行,响应是先加的先执行

2.5 桥接模式

首先,什么是桥接模式,它主要是优化不同维度,从 3*3 变成 3+1
桥接模式是一种结构设计模式,用于将抽象部分和其实现部分解耦,并使它们能够独立地变化。它通过将抽象部分与实现部分之间建立一个桥接(Bridge)来实现解耦。

桥接模式人如其名,其实就相当于一个桥梁,把不同维度的变量桥接在一起来实现功能。假设我们需要实现三种形状(长方形,圆形,三角形),每种形状有三种颜色(红色,绿色,蓝色),这个需求有两个方案,一个方案写九个方法,每个方法实现一个图形:

function redRectangle() {}
function greenRectangle() {}
function blueRectangle() {}
function redCircle() {}
function greenCircle() {}
function blueCircle() {}
function redTriangle() {}
function greenTriangle() {}
function blueTriangle() {}

上述代码虽然功能实现了,但是如果我们需求变了,我们要求再加一个颜色,那我们就得再加三个方法,每个形状加一个。这么多方法看着就很重复,意味着他有优化的空间。我们仔细看下这个需求,我们最终要画的图形有颜色和形状两个变量,这两个变量其实是没有强的逻辑关系的,完全是两个维度的变量。那我们可以将这两个变量拆开,最终要画图形的时候再桥接起来,就是这样:

function rectangle(color) {     // 长方形
  showColor(color);
}

function circle(color) {     // 圆形
  showColor(color);
}

function triangle(color) {   // 三角形
  showColor(color);
}

function showColor(color) {   // 显示颜色的方法
  
}

// 使用时,需要一个红色的圆形
let obj = new circle('red');

使用桥接模式后我们的方法从3 * 3变成了3 + 1,而且如果后续颜色增加了,我们只需要稍微修改showColor方法,让他支持新颜色就行了。如果我们变量的维度不是2,而是3,这种优势会更加明显,前一种需要的方法是x * y * z个,桥接模式优化后是x + y + z个,这直接就是指数级的优化。所以这里桥接模式优化的核心思想是观察重复代码能不能拆成多个维度,如果可以的话就把不同维度拆出来,使用时再将这些维度桥接起来。

举例:

桥接模式其实最形象的例子就是毛笔和蜡笔,因为这个例子非常直观,好理解。这个例子的需求是要画细,中,粗三种型号的线,每种型号的线需要5种颜色,如果我们用蜡笔来画就需要15支蜡笔,如果我们换毛笔来画,只需要3支毛笔就行了,每次用不同颜色的墨水,用完换墨水就行。写成代码就是这样,跟上面那个有点像:

// 先来三个笔的类
function smallPen(color) {
  this.color = color;
}
smallPen.prototype.draw = function() {
  drawWithColor(this.color);    // 用color颜色来画画
}

function middlePen(color) {
  this.color = color;
}
middlePen.prototype.draw = function() {
  drawWithColor(this.color);    // 用color颜色来画画
}

function bigPen(color) {
  this.color = color;
}
bigPen.prototype.draw = function() {
  drawWithColor(this.color);    // 用color颜色来画画
}

// 再来一个颜色类
function color(color) {
  this.color = color;
}

// 使用时
new middlePen(new color('red')).draw();    // 画一个中号的红线
new bigPen(new color('green')).draw();     // 画一个大号的绿线

对于 axios来说:

在 Axios 中,抽象部分可以被看作是对 HTTP 请求的封装,而实现部分则表示不同平台或环境下的具体实现方式,例如浏览器环境和 Node.js 环境。
Axios 通过提供统一的 API 接口,将不同平台下的 HTTP 请求细节抽象出来,使得开发者可以使用相同的代码来发送请求,而无需关心底层实现的差异。
比如,我们需要发送 get、post 请求,并且需要在 浏览器 和 node 端运行,那么本来是 2*2
而对 get、post 请求统一封装,将它变成 1,那么此时就变成了 1*2
我们可以看到无论是发送 GET 请求还是 POST 请求,使用的都是相同的 axios 对象和函数调用方式。这使得开发者可以在不同的平台和环境下保持一致的编码风格和API调用方式。
Axios 库内部根据当前运行环境的不同,会选择合适的实现方式来发送 HTTP 请求,例如在浏览器环境中使用 XMLHttpRequest 或 Fetch API,在 Node.js 环境中使用基于 HTTP 模块或第三方库如 http 或 https。
通过这种设计,Axios 实现了抽象部分(HTTP 请求)与实现部分(底层网络传输)之间的解耦,使得开发者可以专注于业务逻辑而无需关心底层实现细节,体现了类似于桥接模式的设计思想。

桥接模式和适配器模式有什么区别

桥接模式(Bridge Pattern)和适配器模式(Adapter Pattern)虽然在某些方面有一些相似之处,但它们在设计目的和实现方式上有着明确的区别。

  1. 设计目的:
    ● 桥接模式的设计目的是将抽象部分与实现部分分离,使它们可以独立变化。桥接模式主要关注如何通过组合来实现不同维度的变化。它的目标是减少抽象和实现之间的耦合,提供更灵活的扩展性。
    ● 适配器模式的设计目的是在不兼容的接口之间进行适配,使它们能够一起工作。适配器模式主要关注如何通过转换接口来实现不同接口之间的协同工作。它的目标是提供接口转换,以实现两个不兼容接口之间的适配。
  2. 功能和用途:
    ● 桥接模式通过将抽象部分和实现部分分开,允许它们可以独立变化。桥接模式可以在两个维度上进行变化,例如在多个平台上实现多个颜色的图形。
    ● 适配器模式主要用于两个不兼容接口之间的协同工作。适配器模式用于兼容两个不同接口,使它们能够一起工作。
    桥接模式 是 二维 的,适配器模式是 一维 的。
  3. 关注的对象:
    ● 桥接模式关注的是如何将抽象部分和实现部分分离,并让它们可以独立变化。它关注的是系统的结构层面。
    ● 适配器模式关注的是两个不兼容接口之间的适配,使它们能够一起工作。它关注的是接口之间的协同工作。
  4. 实现方式:
    ● 桥接模式通过将抽象部分和实现部分分开,并通过组合的方式实现不同维度的变化。抽象部分和实现部分可以独立扩展而不会相互影响。
    ● 适配器模式通常通过包装或继承来转换接口,使两个接口能够一起工作。适配器模式兼容已有的代码,通过适配器来调用原有接口,以实现两个接口之间的协同工作。

总结起来,桥接模式主要关注在抽象部分和实现部分之间的分离和独立变化,而适配器模式主要关注两个不兼容接口之间的适配和协同工作。它们在设计目的、功能和实现方式上有明显的差异。文章来源地址https://www.toymoban.com/news/detail-620608.html

到了这里,关于从 axios 源码学习设计模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • javascript设计模式-组合

    组合模式 是一种专为创建WEB上的动态用户界面而量身定制的模式。使用它,可以用一条命令在多个对象上激发复杂或递归行为,那些复杂行为被委托给各个对象。前提是每个对象必须实现相同的接口。接口检查越严格,其稳定性越高。 可以用同样的方法处理对象的集合与其

    2024年01月21日
    浏览(41)
  • JavaScript设计模式详解

    JavaScript设计模式是解决软件设计中常见问题的可重复使用的解决方案。本博客将深入探讨JavaScript设计模式的各个方面,包括设计模式的类别、创建型、结构型、行为型、并发型和架构型设计模式。 设计模式是一种在软件设计中解决常见问题的可重用解决方案。它们是经过验

    2024年01月19日
    浏览(48)
  • javaScript设计模式-工厂

    它的好处是消除对象间的耦合度,在派生子类时提供了更大的灵活性。但盲目的把普通的构造函数扔在一边,并不值得提倡。如果要采一不可能另外换用一个类,或都不需要在运行期间在一系列可互换的类中进行选择,就不应该使用。这样在后期代码重构时还有机会使用。

    2024年01月20日
    浏览(54)
  • javaScript设计模式-单例

    确保一个类只有一个实例,并提供全局访问点。 这个模式有三种不同的实现方式,每种都合理。但各有各的用处,其实用static类也可以实现相似的功能,不同的是单例是使用再创建,static是JVM加载时就创建。 单例提供了将代码组织为一个逻辑单元的手段,它有许多用途:可

    2024年01月19日
    浏览(39)
  • JavaScript 设计模式之享元模式

    将一部分共用的方法提取出来作为公用的模块 享元模式的应用目的是为了提高程序的执行效率与系统的性能。因此在大型系统开发中应用是比较广泛的,有时可以发生质的改变。它可以避免程序中的数据重复。有时系统内存在大量对象,会造成大量存占用,所以应用享元模式

    2024年02月22日
    浏览(40)
  • JavaScript中的设计模式

    本文作者为 360 奇舞团前端开发工程师 JavaScript 设计模式是编程世界的智慧结晶,提供了解决常见问题的优秀方案。无论你是初学者还是经验丰富的开发者,掌握这些模式都能让你的代码更清晰、更灵活。本文将为你介绍一些常见的设计模式,帮助你提高代码质量,构建更可

    2024年02月21日
    浏览(39)
  • javascript设计模式-应用示例

    有些时候为了适应没的场景,有些代码没必要每次调用时都进行一次环境判断,所以可以memoizing技术动态改写运行中代码的实现。 发起多个请求,程序会自动缓存,并通过setTimeOut重复调用。 这个例子充分应用了通道方法,利用此模式应该可以做到AOP的功能。

    2024年01月23日
    浏览(41)
  • javascript设计模式-装饰者

    基本实现 是一种为对象增加我的技术,它并不使用创建新子类手段,一切都在动态完成。这个过程相对于使用者来说是透明的。透明地把对象包装在具有同样接口的另一个对象之中。 比如可以动态的为自行车对象添加可选的特色配件上。比如添加4个选件,可以新定义4个超类

    2024年01月22日
    浏览(48)
  • javascript设计模式-责任链

    责任链 可以用来消除请求的发送者和接收者之间的耦合,这是通过实现一个由隐式地对请求进行处理的对象组成的链而做到的。链中的每个对象可以处理请求,也可以将其传给下一个对象。JS内部就使用了这种模式来处一事件捕获和冒泡问题。一般的结构如下: 发送者知道链

    2024年01月23日
    浏览(42)
  • JavaScript设计模式(五)——发布订阅模式、桥接模式、组合模式

    个人简介 👀 个人主页: 前端杂货铺 🙋‍♂️ 学习方向: 主攻前端方向,正逐渐往全干发展 📃 个人状态: 研发工程师,现效力于中国工业软件事业 🚀 人生格言: 积跬步至千里,积小流成江海 🥇 推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js🍒

    2024年02月09日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包