不到30行代码,有5层嵌套的if语句?避免陷入“if-else地狱”!

 

作者 | Aycan Gulez      

多年前,我参与了一个至今仍被广泛使用的打车移动应用的开发。我不知道现在他们运行的代码是什么情况,但如果我没记错的话,早期关于司机分配这部分的代码,很大程度上类似于下面这个极度简化的示例:

  •  
async function assignDriver(rider, availableDrivers) {    const driverDistances = await calculateDistances(rider.location, availableDrivers);    let assignedDriver = null;
    for (let driver of availableDrivers) {        if (driverDistances[driver.id] 5) {            if (!rider.preferredVehicle || rider.preferredVehicle === driver.vehicle) {                if (driver.rating >= 4.5) {                    if (rider.preferences.includes('Premium Driver')) {                        if (driver.isPremiumDriver) {                            assignedDriver = driver;                            break;                        } else {                            continue;                        }                    } else {                        assignedDriver = driver;                        break;                    }                } else if (driver.rating >= 4.0) {                    assignedDriver = driver;                    break;                }            }        }    }
    return assignedDriver;}

在这段不到 30 行的代码中,有五层嵌套的 if 语句。也许有人会说,这看起来还不算太糟,但不难想象,如果再加上诸如高峰期加价、忠诚度计划等更多检查条件,这些代码会变得多么复杂。

幸运的是,我们有办法将代码扁平化处理,而最终的结果可能会让你大吃一惊:当我们完成重构后,就不会再有 if 语句了。

不到30行代码,有5层嵌套的if语句?避免陷入“if-else地狱”!

利用保护子句

让我们先从简单的开始。第一个 if 语句用于检查司机和乘客之间允许的最大距离,由于它适用于所有情况,因此可以将其转换为保护子句,从而移除一层嵌套。同样,我们还可以为首选车辆检查添加另一个保护子句,以去除另一层嵌套。由此修改后的代码如下所示:

  •  
async function assignDriver(rider, availableDrivers) {    const driverDistances = await calculateDistances(rider.location, availableDrivers);    let assignedDriver = null;
    for (let driver of availableDrivers) {    if (driverDistances[driver.id] > 5) {      continue;    }    if (rider.preferredVehicle && rider.preferredVehicle !== driver.vehicle) {        continue;    }    if (driver.rating >= 4.5) {            if (rider.preferences.includes('Premium Driver')) {                if (driver.isPremiumDriver) {                assignedDriver = driver;                break;            } else {                continue;            }        } else {            assignedDriver = driver;            break;        }    } else if (driver.rating >= 4.0) {        assignedDriver = driver;        break;    }    }
    return assignedDriver;}

不到30行代码,有5层嵌套的if语句?避免陷入“if-else地狱”!

决策表

与其在函数内部硬编码逻辑,我们可以将每个 if-else 模块放在它自己的函数中,并将这些函数放入一个数组,形成一个决策表。然后,我们遍历决策表中的每个函数,直到得到一个肯定的响应。当然了,表中的条目必须从最具体的到最不具体的顺序排列,这样才能在我们的案例中正常运行。

  •  
const conditions = [    (rider, driver) => driver.rating >= 4.5 && rider.preferences.includes('Premium Driver') && driver.isPremiumDriver,    (rider, driver) => driver.rating >= 4.5 && !rider.preferences.includes('Premium Driver'),    (rider, driver) => driver.rating >= 4.0 && driver.rating 4.5,];
async function assignDriver(rider, availableDrivers, conditions) {    const driverDistances = await calculateDistances(rider.location, availableDrivers);    let assignedDriver = null;
    for (let driver of availableDrivers) {        if (driverDistances[driver.id] > 5) {            continue;        }        if (rider.preferredVehicle && rider.preferredVehicle !== driver.vehicle) {            continue;        }        if (conditions.find((condition) => condition(rider, driver))) {            assignedDriver = driver;            break;        }    }
    return assignedDriver;}

有了决策表,我们就彻底消除了嵌套的 if 语句,还有一个额外的好处:现在只需简单编辑条件数组,就可以更改驱动程序的赋值逻辑。

不到30行代码,有5层嵌套的if语句?避免陷入“if-else地狱”!

函数组合

让我们把 for 循环转换为 Array.find(),并为循环中的每个 if 语句创建单独的函数,从而消除剩余的 if 语句:

  •  
const conditions = [    (rider, driver) => driver.rating >= 4.5 && rider.preferences.includes('Premium Driver') && driver.isPremiumDriver,    (rider, driver) => driver.rating >= 4.5 && !rider.preferences.includes('Premium Driver'),    (rider, driver) => driver.rating >= 4.0 && driver.rating 4.5,];
async function assignDriver(rider, availableDrivers, conditions) {    const driverDistances = await calculateDistances(rider.location, availableDrivers);
    const isDriverClose = (driver) => driverDistances[driver.id] 5;    const isVehicleOK = (rider, driver) => !rider.preferredVehicle || rider.preferredVehicle === driver.vehicle;    const isAConditionSatisfied = (rider, driver) => conditions.find((condition) => condition(rider, driver));
    const assignedDriver = availableDrivers.find(        (driver) => isDriverClose(driver) && isVehicleOK(rider, driver) && isAConditionSatisfied(rider, driver)    );
    return assignedDriver || null;}

不到30行代码,有5层嵌套的if语句?避免陷入“if-else地狱”!

总结

通过利用基本的函数式编程原则,我们已经:

● 移除了所有的嵌套 if 语句,

● 将大部分逻辑解耦,使其易于修改,

● 通过将各个 if-else 块放入其自己的函数中,使代码更易于理解,

● 最后还有个额外的好处,即程序大小减少了三分之一。

不到30行代码,有5层嵌套的if语句?避免陷入“if-else地狱”!

 

本文摘自网络。

原创文章,作者:北大青鸟,如若转载,请注明出处:http://news.yy-accp.com/archives/18614