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

1e1762ad5154d94560669487e01ed2ca.jpeg

【编者按】随着应用程序复杂度的日益增长,传统编程模式下层层嵌套的 if-else 语句逐渐成为了开发者们的“梦魇”,不仅令逻辑变得晦涩难懂,更增加了后期调试与维护的成本。面对这一挑战,本文作者认为函数式编程风格以其独特的魅力,提供了一条避免陷入“if-else 地狱”的优雅路径。

原文链接:https://lackofimagination.org/2024/09/avoiding-if-else-hell-the-functional-style/

作者 | Aycan Gulez      翻译 | 郑丽媛

出品 | 优快云(ID:优快云news)

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

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

492f97523f6f7f662ccc80c09ded4544.png

利用保护子句

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

96bda5b8a8e469e6169c5e73f9d3c571.png

决策表

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

7c4fffc2a4653b00e169830dcd4f1dce.png

函数组合

让我们把 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;
}

eb90fa2b8464d59945c8df1633fa7987.png

总结

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

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

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

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

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

dddc558ae375df38f362a8d8b04e8129.gif

勿再“浮沙筑高台”

用扎实的 C++ 技术为你的职业发展奠定坚实基础

加入「C++ 大师系列精品课」

带你踏上一条通往技术巅峰的学习之旅!

dd234fa1f5b67093d52fb4858f0053d7.jpeg

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值