面试题如何避免使用过多的 if else?
Posted 前端技术栈
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试题如何避免使用过多的 if else?相关的知识,希望对你有一定的参考价值。
大厂面试题分享 面试题库
前后端面试题库 (面试必备) 推荐:★★★★★
地址:前端面试题库
一、引言
相信大家听说过回调地狱——回调函数层层嵌套,极大降低代码可读性。其实,if-else层层嵌套,如下图所示,也会形成类似回调地狱的情况。
当业务比较复杂,判断条件比较多,项目进度比较赶时,特别容易使用过多if-else。其弊端挺多的,如代码可读性差、代码混乱、复杂度高、影响开发效率、维护成本高等。
因此,我们在日常编码时,有必要采取一些措施避免这些问题。本文的初衷不是建议大家完全不用if-else,而是希望我们能够在学会更多解决方案后更优雅地编码。
二、8种if-else的优化/替代方案
1. 使用排非策略:!、!!
逻辑非(logic NOT),是逻辑运算中的一种,就是指本来值的反值。
当你想这么写时……
1、判断是否为空
if(value === null || value === NaN || value === 0 || value === ''|| value === undefined )
……
2、判断是否数组是否含有符合某条件的元素
const name = arr.find(item => item.status === 'error')?.name;
if(name !== undefined && name !== '')
……
复制代码
不妨尝试这么写:
1、判断是否为空
if(!value)……
2、判断是否数组是否含有符合某条件的元素
if(!!arr.find(item => item.status === 'error')?.name)……
复制代码
2. 使用条件(三元)运算符: c ? t : f
三元运算符: condition ? exprIfTrue : exprIfFalse; 如果条件为真值,则执行冒号(:)前的表达式;若条件为假值,则执行最后的表达式。
当你想这么写时……
let beverage = '';
if(age > 20)
beverage = 'beer';
else
beverage = 'juice';
复制代码
不妨尝试这么写:
const beverage = age > 20 ? 'beer' : 'juice';
复制代码
tips: 建议只用一层三元运算符,多层嵌套可读性差。
3. 使用短路运算符:&&、 ||
&& 为取假运算,从左到右依次判断,如果遇到一个假值,就返回假值,以后不再执行,否则返回最后一个真值;
|| 为取真运算,从左到右依次判断,如果遇到一个真值,就返回真值,以后不再执行,否则返回最后一个假值。
当你想这么写时……
if (isOnline)
makeReservation(user);
复制代码
不妨尝试这么写:
isOnline && makeReservation(user);
复制代码
4. 使用 switch 语句
当你想这么写时……
let result;
if (type === 'add')
result = a + b;
elseif(type === 'subtract')
result = a - b;
elseif(type === 'multiply')
result = a * b;
elseif(type === 'divide')
result = a / b;
else
console.log('Calculation is not recognized');
复制代码
不妨尝试这么写:
let result;
switch (type)
case'add':
result = a + b;
break;
case'subtract':
result = a - b;
break;
case'multiply':
result = a * b;
break;
case'divide':
result = a / b;
break;
default:
console.log('Calculation is not recognized');
复制代码
个人认为,对于这类比较简单的判断,用switch语句虽然不会减少代码量,但是会更清晰喔。
5. 定义相关函数拆分逻辑,简化代码
当你想这么写时……
functionitemDropped(item, location)
if (!item)
returnfalse;
elseif (outOfBounds(location)
var error = outOfBounds;
server.notify(item, error);
items.resetAll();
returnfalse;
else
animateCanvas();
server.notify(item, location);
returntrue;
复制代码
不妨尝试这么写:
// 定义dropOut和dropIn, 拆分逻辑并提高代码可读性functionitemDropped(item, location)
const dropOut = function ()
server.notify(item, outOfBounds);
items.resetAll();
returnfalse;
;
const dropIn = function ()
animateCanvas();
server.notify(item, location);
returntrue;
;
return !!item && (outOfBounds(location) ? dropOut() : dropIn());
复制代码
细心的朋友会发现,在这个例子中,同时使用了前文提及的优化方案。这说明我们在编码时可以根据实际情况混合使用多种解决方案。
6. 将函数定义为对象,通过穷举查找对应的处理方法
① 定义普通对象
对于方案3的例子,不妨尝试这么写:
functioncalculate(action, num1, num2)
const actions =
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => a / b,
;
return actions[action]?.(num1, num2) ?? "Calculation is not recognized";
复制代码
② 定义 Map 对象
普通对象的键需要是字符串,而 Map 对象的键可以是一个对象、数组或者更多类型,更加灵活。
let statusMap = newMap([
[
role: "打工人", status: "1" ,
() => /*一些操作*/,
],
[
role: "打工人", status: "2" ,
() => /*一些操作*/,
],
[
role: "老板娘", status: "1" ,
() => /*一些操作*/,
],
]);
let getStatus = function (role, status)
statusMap.forEach((value, key) =>
if (JSON.stringify(key) === JSON.stringify( role, status ))
value();
);
;
getStatus("打工人", "1"); // 一些操作复制代码
tips: JSON.stringify()可用于深比较/深拷贝。
7. 使用责任链模式
责任链模式:将整个处理的逻辑改写成一条责任传递链,请求在这条链上传递,直到有一个对象处理这个请求。
例如 JS 中的事件冒泡。
简单来说,事件冒泡就是在一个对象上绑定事件,如果定义了事件的处理程序,就会调用处理程序。相反没有定义的话,这个事件会向对象的父级传播,直到事件被执行,最后到达最外层,document对象上。
这意味着,在这种模式下,总会有程序处理该事件。
再举个🌰,当你想这么写时……
functiondemo (a, b, c)
if (f(a, b, c))
if (g(a, b, c))
// ...
// ...elseif (h(a, b, c))
// ...
// ...
elseif (j(a, b, c))
// ...
elseif (k(a, b, c))
// ...
复制代码
不妨参考这种写法:
const rules = [
match: function (a, b, c) /* ... */ ,
action: function (a, b, c) /* ... */
,
match: function (a, b, c) /* ... */ ,
action: function (a, b, c) /* ... */
,
match: function (a, b, c) /* ... */ ,
action: function (a, b, c) /* ... */
// ...
]
// 每个职责一旦匹配,原函数就会直接返回。functiondemo (a, b, c)
for (let i = 0; i < rules.length; i++)
if (rules[i].match(a, b, c))
return rules[i].action(a, b, c)
复制代码
引申话题——如何降低if else代码的复杂度?
相关文章阅读: 如何无痛降低 if else 面条代码复杂度 建议多读几次!!!
三、小结
本文粗略介绍了8种优化/替代if-else的方法,希望能给你日常编码带来一些启示😄。
正如开头所说,我们的目的不是消灭代码中的if-else,而是让我们在学会更多解决方案的基础上,根据实际情况选择更优的编码方式。因此,当你发现自己的代码里面存在特别多的if-else或当你想用if-else时,不妨停下来思考一下——如何能写得更优雅、更方便日后维护呢。
大厂面试题分享 面试题库
前后端面试题库 (面试必备) 推荐:★★★★★
地址:前端面试题库
使用策略模式+工厂模式干掉代码中过多的if-else
过多if-else项目背景
如果一开始就知道现在的业务需要,大部分人都不会在代码里添加过多的if-else判断的,烂代码基本都是刚开始写代码时并没有太多的需求,随着期需求不断的修改增加,开发时间也较的紧张,代码往往都是怎么快速怎么写。当然多写一个if-else比使用各种设计模式肯定来的更快速了,这也就导致项目代码慢慢变得臃肿,难以维护的主要原因。在有空闲时间的情况下就可以给以前的代码做一次手术了。
先看本次未优化前的代码:
@Override
public MMediaInfo copyToLibType(UserQiniuDTO userQiniuDTO, Long mediaId, Integer libType) {
MMediaInfo mMediaInfo = new MMediaInfo();
LibTypeEnum libTypeEnum = LibTypeEnum.valueof(libType);
if (libTypeEnum.getFileType().equals(FileType.VIDEO)) {
mMediaInfo = mvideoInfoService.copyToLibType(userQiniuDTO, mediaId, libType);
}
if (libTypeEnum.getFileType().equals(FileType.AUDIO)) {
mMediaInfo = mAudioInfoService.copyToLibType(userQiniuDTO, mediaId, libType);
}
if (libTypeEnum.getFileType().equals(FileType.PICTURE)) {
mMediaInfo = mImgInfoService.copyToLibType(userQiniuDTO, mediaId, libType);
}
if (libTypeEnum.getFileType().equals(FileType.FILE)) {
mMediaInfo= mFileInfoService.copyToLibType(userQiniuDTO, mediaId, libType);
}
return mMediaInfo;
}
之所有会有些段代码,主要原因是项目前期,需求中只有 视频/音频的 ,并且音频操作接口,逻辑都是分开的,后面又增加了图片/文件需求,并且要求视频/音频/文件/图片 能合并在同一接口处理, 这就导致了代码中很多操作有大量 if-else判断
再看优化后的代码:
@Override
public MMediaInfo copyToLibType(UserQiniuDTO userQiniuDTO, Long mediaId, Integer libType) {
LibTypeEnum libTypeEnum = LibTypeEnum.valueof(libType);
IFileService iFileService = FileFactory.getFileService(libTypeEnum.getFileType());
return iFileService.copyToLibType(userQiniuDTO, mediaId, libType);
}
此断代码看起来就清爽多了,实现的基本思路就是使用了 策略模式+工厂模式,代码通过不同的文件类型返回对应的实现类来实现复制逻辑
基本视频步骤
- 创建IFileService接口
public interface IFileService {
Integer getFileType();
MMediaInfo copyToLibType(UserQiniuDTO userQiniuDTO, Long mediaId, Integer libType);
}
- 视频/音频/图片/文件类分别视频IFileService接口
public class MVideoInfoServiceImpl extends ServiceImpl<MvideoInfoMapper, MvideoInfo> implements MvideoInfoService, IFileService {
@Override
public Integer getFileType() {
return FileType.VIDEO;
}
@Override
public MMediaInfo copyToLibType(UserQiniuDTO userQiniuDTO, Long id, Integer libType) {
//复制视频逻辑
return mMediaInfo;
}
- 创建FileFactory工厂类
@Component
public class FileFactory implements ApplicationContextAware {
private static Map<Integer, IFileService> fileServiceMap;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, IFileService> map = applicationContext.getBeansOfType(IFileService.class);
fileServiceMap = new HashMap<>();
map.forEach((key, value) -> fileServiceMap.put(value.getFileType(), value));
}
public static <T extends IFileService> T getFileService(Integer fileType) {
return (T)fileServiceMap.get(fileType);
}
}
总结
copyToLibType只是优化的一个方式,IFileService中还有很多 如 移动/删除 等操作的代码也可以干掉过多的if-else了。
以上是关于面试题如何避免使用过多的 if else?的主要内容,如果未能解决你的问题,请参考以下文章