案例引入
昨天在调试同事开发的页面,发现有很多 404 的请求,看了下 Network 面板有点哭笑不得,原来是前端把页面路由当成后台接口请求了,后台肯定不存在对应的资源,因为是前端路由。那么为什么会发送这样的请求呢?
我看了下同事写的代码,由于这边的需求是需要同时支持 mock 环境和正常后台环境,然后根据传递的 query
字段选择合适的接口进行请求。然鹅看到代码就惊呆了,各种 IF ELSE 和 SWITCH CASE ,夸张的是同事告诉我这是他能想到的最优方案(手工狗头)
const isMockEnv = process.env.USING_MOCK_ENV === 'true';
function fetchData(query) {
let reqUrl = "";
if (isMockEnv) {
switch (query) {
case "api_1":
reqUrl = "/api/api_1";
break;
case "api_2":
reqUrl = "/api/api_2";
break;
case "api_3":
reqUrl = "/api/api_3";
default:
break;
}
} else {
switch (query) {
case "api_1":
reqUrl = "/mock/api/api_1";
break;
case "api_2":
reqUrl = "/mock/api/api_2";
break;
case "api_3":
reqUrl = "/mock/api/api_3";
default:
break;
}
}
return request({
url: reqUrl,
method: "get"
})
}
上面只是一个简单示意,实际接口还要多,而且不同的接口需要带不同的 query 参数,不能简单地通过字符串拼接实现
然后通过 debug 可以发现,有时候给 fetchData
传递的参数 SWITCH CASE 里面没有匹配上,导致 reqUrl
最后拿到的是空字符串,因此最后 axios
就默认按照当前页面的路由去请求了。
如果仅仅只是修复 bug ,完全没必要为这个写篇文章。这边主要是本人非常看不惯同事写的逻辑,于是决定用策略模式优化一下:
const switchQueryStrategy = () => {
const isMockEnv = process.env.USING_MOCK_ENV === 'true';
const urls = {
"api_1": "/api/api_1",
"api_2": "/api/api_2",
"api_3": "/api/api_3"
}
const mockUrls = {
"api_1": "/mock/api/api_1",
"api_2": "/mock/api/api_2",
"api_3": "/mock/api/api_3"
}
return isMockEnv ? mockUrls : urls;
}
const fetchData = (query) => {
const queryStrategy = switchQueryStrategy();
// 处理参数匹配不上的情形
if (!Object.keys(queryStrategy).includes(query)) {
return Promise.reject("API Not Found!");
}
return request({
url: queryStrategy[query],
method: 'get'
})
}
优化之后感觉好多了,然后同事看了代码却说跟我也差不多嘛,这也叫优化?
差不多?明明差很多好吗,这叫策略模式,不是拍脑袋想出来的,而是有统一规范的。
之所以我们平时开发的时候不需要过多关注设计模式,那是因为框架帮我们把逻辑封装好了,我们只需要按照一个统一的编程风格去开发就行。例如 Vue 的响应式机制是基于观察者模式,事件机制则是通过发布订阅模式等等。但是这不等于不需要了解设计模式了,假如你不了解设计模式,且不说设计框架,就是一个 API 的封装都不一定做得好。
策略模式介绍
策略模式指的是定义了一系例算法,把它们每个都封装起来。将不变的部分和变化的部分隔离开来是设计模式中的一个重要思想,策略模式则是将算法和使用算法两部分的实现拆开,降低耦合度。
基于策略模式的程序至少有两部分组成:策略类和环境类(Context)。
策略类封装了具体的算法,并负责具体的计算过程,可以理解为“执行者”。
环境类(Context)接受客户的请求,然后将请求委托给一个策略类,可以理解为“调度者”。
优点:
- 隔离算法的实现与使用
- 运行时可切换算法
- 用组合代替继承
- 易扩展,符合开闭原则,无需修改上下文即可引入新策略
缺点:
- 需要暴露所有的策略接口,用于区分策略差异
- 如果逻辑条件简单,使用策略模式会增加代码冗余度
表驱动法
策略模式一大特点就是节省判断逻辑。有一个与策略模式类似的概念,叫“表驱动法”,或者叫“查表法”。
表驱动方法是一种使你可以在表中查找信息,而不必用很多的逻辑语句(
if
或Case
)来把它们找出来的方法。事实上,任何信息都可以通过表来挑选。在简单的情况下,逻辑语句往往更简单而且更直接。但随着逻辑链的复杂,表就变得越来越富有吸引力了。
举个简单的例子,假设我们要获取当前是星期几,代码可能是这样的:
function getDay() {
const day = (new Date()).getDay();
switch (day) {
case 0:
return '星期日';
case 1:
return '星期一';
// ...
case 6:
return '星期六';
default:
return '';
}
}
如果是初次编程的同学还可能会有 if else
条件语句来判断返回值,代码就会显得比较冗余。
借助表驱动法的思想,这里我们是可以有优化空间的,表驱动法的写法如下:
const days = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
function getDay2() {
return days[(new Date()).getDay()];
}
将数组的元素和下标对应起来是非常常用的技巧
当然上述只是一个非常简单的🌰,大家在编码过程中只需要主要点,如有涉及类似场景,请用这种方式去编码,体验更愉悦!