为什么要提高代码扩展性
我们写的代码都是为了一定的需求服务的,但是这些需求并不是一成不变的,当需求变更了,如果我们代码的扩展性很好,我们可能只需要简单的添加或者删除模块就行了,如果扩展性不好,可能所有代码都需要重写,那就是一场灾难了,所以提高代码的扩展性是势在必行的。怎样才算有好的扩展性呢?好的扩展性应该具备以下特征:
- 需求变更时,代码不需要重写。
- 局部代码的修改不会引起大规模的改动。有时候我们去重构一小块代码,但是发现他跟其他代码都是杂糅在一起的,里面各种耦合,一件事情拆在几个地方做,要想改这一小块必须要改很多其他代码。那说明这些代码的耦合太高,扩展性不强。
- 可以很方便的引入新功能和新模块。
怎么提高代码扩展性?
当然是从优秀的代码身上学习了,本文会深入Axios
,Node.js
,Vue
等优秀框架,从他们源码总结几种设计模式出来,然后再用这些设计模式尝试解决下工作中遇到的问题。本文主要会讲职责链模式
,观察者模式
,适配器模式
,装饰器模式
。下面一起来看下吧:
职责链模式
职责链模式顾名思义就是一个链条,这个链条上串联了很多的职责,一个事件过来,可以被链条上的职责依次处理。他的好处是链条上的各个职责,只需要关心自己的事情就行了,不需要知道自己的上一步是什么,下一步是什么,跟上下的职责都不耦合,这样当上下职责变化了,自己也不受影响,往链条上添加或者减少职责也非常方便。
实例:Axios拦截器
用过Axios的朋友应该知道,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
})
}
上面的代码其实就完成了拦截器创建和use
的逻辑,并不复杂,那这些拦截器方法都是什么时候执行呢?当然是我们调用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;
}
上述代码是从Axios源码中精简出来的,可以看出他巧妙的运用了职责链模式,将需要做的任务组织成一个链条,这个链条上的任务相互不影响,拦截器可有可无,而且可以有多个,兼容性非常强。
实例:职责链组织表单验证
看了优秀框架对职责链模式的运用,我们再看看在我们平时工作中这个模式怎么运用起来。现在假设有这样一个需求是做一个表单验证,这个验证需要前端先对格式等内容进行校验,然后API发给后端进行合法性校验。我们先分析下这个需求,前端校验是同步的,后端验证是异步的,整个流程是同步异步交织的,为了能兼容这种情况,我们的每个验证方法的返回值都需要包装成promise才行
// 前端验证先写个方法
function frontEndValidator(inputValue) {
return Promise.resolve(inputValue); // 注意返回值是个promise
}
// 后端验证也写个方法
function backEndValidator(inputValue) {
return Promise.resolve(inputValue);
}
// 写一个验证器
function validator(inputValue) {
// 仿照Axios,将各个步骤放入一个数组
const validators = [frontEndValidator, backEndValidator];
// 前面Axios是循环调用promise.then来执行的职责链,我们这里换个方式,用async来执行下
async <