作为一个程序员在开发中?if else
?判断在代码中是必不可少的,但是?if else
?判断使用多了嵌套多了不利于代码维护,看起来也头疼难以理解,接下来以为大家介绍一下我是怎么避免过多冗余的?if else
?嵌套的。
在最终执行有用操作之前,想对一些数据执行各种检查,以确保其有效性。这种情况下,可以使用if语句的嵌套来按顺序执行多个检查,只有当所有检查都通过时才执行有用的操作。这有助于确保数据在被处理之前是安全的。
冗余的写法:
function sendMoney(account, amount) {
if (account.balance > amount) {
if (amount > 0) {
if (account.sender === 'user-token') {
account.balance -= amount;
console.log('Transfer completed');
} else {
console.log('Forbidden user');
}
} else {
console.log('Invalid transfer amount');
}
} else {
console.log('Insufficient funds');
}
}
优化后:
function sendMoney(account, amount) {
if (account.balance < amount) {
console.log('Insufficient funds');
return;
}
if (amount <= 0) {
console.log('Invalid transfer amount');
return;
}
if (account.sender !== 'user-token') {
console.log('Forbidden user');
return;
}
account.balance -= amount;
console.log('Transfer completed');
}
与嵌套的if语句不同,我们有多个if语句,每个if语句执行检查并在条件不满足时立即返回。在这种模式中,我们可以将每个if语句称为防御式编程(guard clause)。这种方式使代码更易阅读和维护,减少了嵌套深度。
如果有使用 node.js 的童鞋,可能会在Express中间件中看到了这种流程:
function authMiddleware(req, res, next) {
const authToken = req.headers.authorization;
if (!authToken) {
return res.status(401).json({ error: 'Unauthorized' });
}
if (authToken !== 'secret-token') {
return res.status(401).json({ error: 'Invalid token' });
}
if (req.query.admin === 'true') {
req.isAdmin = true;
}
next();
}
再看看这样的写法:
function authMiddleware(req, res, next) => {
const authToken = req.headers.authorization;
if (authToken) {
if (authToken === 'secret-token') {
if (req.query.admin === 'true') {
req.isAdmin = true;
}
return next();
} else {
return res.status(401).json({ error: 'Invalid token' });
}
} else {
return res.status(401).json({ error: 'Unauthorized' });
}
};
是不是瞬间感觉这样的代码就比较恶心了,所以我们尽量要避免回调地狱的写法,采用防御式编程的写法,这种模式使代码更加清晰和易于维护。
在这里,我们可以清楚地看到它是cond3
的if语句。在这之后,如果不再进行更多的检查,我们可以执行我们一直想要执行的操作。
function func(cond1, cond2, cond3) {
if (cond1) {
if (cond2) {
if (cond3) {
console.log('PASSED!');
console.log('taking success action...');
} else {
console.log('failed condition 3');
}
} else {
console.log('failed condition 2');
}
} else {
console.log('failed condition 1');
}
}
否定?if
?条件以将?else
?语句的主体放入其中,并在其后添加一个?return
?语句。
删除?else
?语句的大括号(保留主体,因为它仍然包含以前嵌套的if语句),并将?if
?语句的右括号移到?return
?语句的后面。
function func(cond1, cond2, cond3) {
if (!cond1) { // 👈 反转后的if条件
// 👇 以前的else子句的主体
console.log('failed condition 1');
return; // 👈 失败时退出
}
// 👇 需要转换的剩余嵌套if语句
if (cond2) {
if (cond3) {
console.log('PASSED!');
console.log('taking success action...');
} else {
console.log('failed condition 3');
}
} else {
console.log('failed condition 2');
}
}
function func(cond1, cond2, cond3) {
if (!cond1) {
console.log('failed condition 1');
return;
}
if (!cond2) {
console.log('failed condition 2');
return;
}
if (!cond3) {
console.log('failed condition 3');
return;
}
console.log('PASSED!');
console.log('taking success action...');
}
在VS Code
中安装JavaScript Booster
扩展后,反转if语句变得非常容易。在这里,我们只需要将光标放在if关键字中,然后激活"显示代码操作"命令(默认情况下是Ctrl + .)。
如果我们在if/else中检查数据后还想执行其他操作怎么办?例如:
function func(cond1, cond2) {
if (cond1) {
if (cond2) {
console.log('PASSED!');
console.log('taking success action...');
} else {
console.log('failed condition 2');
}
console.log('after cond2 check');
} else {
console.log('failed condition 1');
}
console.log('after cond1 check');
}
在这个函数中,无论cond1
的值如何,'after cond1 check'这一行都会打印。如果cond1
为true
,则cond2
的值也是类似的情况。
在这种情况下,要使用防御使编程需要更多的工作:
如果我们尝试使用防御使编程,将会重复出现在if/else
检查之后的行:
function func(cond1, cond2) {
if (!cond1) {
console.log('failed condition 1');
console.log('after cond1 check');
return;
}
if (!cond2) {
console.log('failed condition 2');
console.log('after cond2 check');
console.log('after cond1 check');
return;
}
console.log('PASSED!');
console.log('taking success action...');
console.log('after cond2 check');
console.log('after cond1 check');
}
func(true);
由于这些行必须被打印,我们在返回之前在防护子句中打印它们。然后,我们在所有(!)后续的防护子句中再次打印它们。如果所有的防护子句都通过,那么在主函数体中再次打印。
那么,我们该怎么办?如何使用防御式编程并仍然遵守DRY原则?
嗯,我们将逻辑分割成多个函数:
function func(cond1, cond2) {
checkCond1(cond1, cond2);
console.log('after cond1 check');
}
function checkCond1(cond1, cond2) {
if (!cond1) {
console.log('failed condition 1');
return;
}
checkCond2(cond2);
console.log('after cond2 check');
}
function checkCond2(cond2) {
if (!cond2) {
console.log('failed condition 2');
return;
}
console.log('PASSED!');
console.log('taking success action...');
}
让我们将这个方法应用到之前看到的Express中间件中:
function authMiddleware(req, res, next) {
checkAuthValidTokenAdmin(req, res, next);
}
function checkAuthValidTokenAdmin(req, res, next) {
const authToken = req.headers.authorization;
if (!authToken) {
return res.status(401).json({ error: 'Unauthorized' });
}
checkValidTokenAdmin(req, res, next);
}
function checkValidTokenAdmin(req, res, next) {
const authToken = req.headers.authorization;
if (authToken !== 'secret-token') {
return res.status(401).json({ error: 'Invalid token' });
}
checkAdmin(req, res, next);
}
function checkAdmin(req, res, next) {
if (req.query.admin === 'true') {
req.isAdmin = true;
}
next();
}
在某种程度上,我们用责任链模式替代了if/else
语句。当然,对于像基本的Express
请求中间件这样的简单逻辑,这可能有些过度,但这里的优点在于它将每个额外的检查委托给一个单独的函数,分离了职责,防止了过多的嵌套。
主要收获 在代码中使用嵌套的if
语句可能导致复杂且难以维护的代码。相反,我们可以使用防御式编程使我们的代码更加可读和线性。我们可以将防御式编程应用于不同的情境,并将它们拆分为多个函数,以避免重复和分担职责。通过采用这种模式,我们最终编写更干净和更易维护的代码。