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

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

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 语句了。

图片

利用保护子句

让我们先从简单的开始。第一个 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;
}

图片

决策表

与其在函数内部硬编码逻辑,我们可以将每个 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 语句,还有一个额外的好处:现在只需简单编辑条件数组,就可以更改驱动程序的赋值逻辑。

图片

函数组合

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

图片

总结

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

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

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值