likes
comments
collection
share

🙅‍都说太多if...else不好,那有没有可以直接抄的改造方案呢?

作者站长头像
站长
· 阅读数 14

嗨,大家好!这里是道长王jj~ 🎩🧙‍♂️

日常的代码审查工作总是会带来各种惊喜🎁,尤其当你遇到那些让你眼前一黑的代码。

比如我在上次的审查中,看到我们的小明同事在他的JavaScript代码中堆砌了一个看起来就像一座大山的超过20个的 if...else 语句。类似下面这样:

function calculatePrice(type, basePrice) {
    if (type === 'student') {
        return basePrice * 0.8;
    } else if (type === 'senior') {
        return basePrice * 0.7;
    } else if (type === 'veteran') {
        return basePrice * 0.75;
    } 
    // 这个列表有20多个条件,我们在这里省略...
    else {
        return basePrice;
    }
}

🙅‍♂️ 为何不推荐大量的 if...else

咳咳,看到这里,让我想起了小时候堆积木游戏,每堆一个积木都充满了危险和快感。但这样的代码确实存在一些问题,比如:

  1. 阅读困难:就像尝试阅读一篇只有一个长长的段落的小说,你需要花费大量的时间和精力去理解它。
  2. 高维护成本:想象一下,如果需要在中间插入一个新的条件,你需要找到正确的位置,然后小心翼翼地插入新的 if...else 语句。
  3. 可扩展性差:对于特殊的需求,例如动态的添加新的折扣类型,你可能需要大量的修改和测试。

作为一名合格的开发者,我们总是希望自己能写出优雅、可读性高、易维护的代码。于是,让我们看看有没有其他方法能替代这个冗长的 if...else 呢?😉

❗注意!不是说所有的if…else都要被替代

重构代码的目的是为了提高代码的可读性、可维护性和扩展性。

但并不是所有的if...else语句都应该被替代或重构!

如果你的代码里是以下的情况,就不要想着重构炫技啦!

  1. 简单的条件分支:如果代码中的if...else语句仅有一到两个简单的条件分支,没必要改。
  2. 高度相关的逻辑分支:复杂的逻辑,如果被分解成多个独立的函数或对象,会失去逻辑的连贯性和上下文含义,你也不想代码看不懂吧!

👏 项目案例:函数式编程+设计模式替代 if...else

注意:以下的代码都有意地在使用JavaScript的对象字面量,可以使代码更加干净和易于阅读。这是一种函数式编程的方式。

设计模式是为了解决一些特定问题而提出的代码结构,它能帮助我们更好地组织代码,提高代码的可读性和可维护性。按照水文掘金老套路。我先介绍一下有哪几种常见的设计模式可以优化这类问题。

策略模式

策略模式定义了一系列的算法,并将每一个算法封装起来,使它们可以互相替换。在 JavaScript 中,我们可以使用对象来实现策略模式,这也是最简单的改造,就像下面这样:

const strategies = {
    'student': (basePrice) => {
        return basePrice * 0.8;
    },
    'senior': (basePrice) => {
        return basePrice * 0.7;
    },
    'veteran': (basePrice) => {
        return basePrice * 0.75;
    },
    'default': (basePrice) => {
        return basePrice;
    }
}

const calculatePrice = (type, basePrice) => {
    return (strategies[type] || strategies['default'])(basePrice);
}
calculatePrice('student', 1) // 输出: 0.8

如果是面对多层嵌套呢?

对于多层嵌套的if...else,我们同样可以通过使用策略模式来改善代码的可读性和维护性。只是说我们把判断逻辑放在了独立的函数中。

假设我们现在有一个需求,即根据用户的类型和他们的购买量来计算折扣。如果用户是学生,购买量大于10个,他们可以享受8折优惠;如果用户是老年人,购买量大于20个,他们可以享受7折优惠。

const strategies = {
    'student': quantity => quantity > 10 ? 0.8 : 1,
    'senior': quantity => quantity > 20 ? 0.7 : 1,
    'default': () => 1
};

const calculate = (type, quantity) => {
    return (strategies[type] || strategies['default'])(quantity)
}
calculate('student', 15) // 输出: 0.8

这样一来,我们的代码就变得更加清晰和易读了。而且如果我们想添加新的策略,只需要在 strategies 对象中添加新的属性和函数就可以了。

工厂模式

其实这个我都有点不愿意讲了,因为上一个例子中其实本身也是一种工厂模式的实现,这一次我们要把那个例子转换为一个更明显的工厂模式,我们可以将函数calculate转换为一个工厂函数,它创建并返回一个对象,这个对象有一个calculate方法:

const strategies = {
    'student': quantity => quantity > 10 ? 0.8 : 1,
    'senior': quantity => quantity > 20 ? 0.7 : 1,
    'default': () => 1
};

const createCalculator = (type) => {
    return {
        calculate: (quantity) => (strategies[type] || strategies['default'])(quantity)
    }
}

const studentCalculator = createCalculator('student');
studentCalculator.calculate(15); // 输出: 0.8

查找表模式

查找表模式其实是策略模式的一种简化形式,它更像是一个简单的映射表。我们可以把所有的策略映射到一个对象中,然后通过键来查找对应的策略。对于上面的例子而言,本质只是改变了存储方式,我们使用映射表来保存策略就行了(是不是突然觉得也没有那么高大上了哈哈😄):

const studentStrategy = quantity => quantity > 10 ? 0.8 : 1;
const seniorStrategy = quantity => quantity > 20 ? 0.7 : 1;
const defaultStrategy = () => 1;

const strategies = {
    'student': studentStrategy,
    'senior': seniorStrategy,
    'default': defaultStrategy
};

const calculate = (type, quantity) => {
    return (strategies[type] || strategies['default'])(quantity)
}

calculate('student', 15); // 输出: 0.8

职责链模式

职责链模式是一种对象的行为模式,在这种模式中,每一个对象都有机会处理请求,从而解除了发送者和接收者之间的耦合关系。在JavaScript中,我们可以用链式调用来实现职责链模式:

// 学生条件进入
const studentHandler = (type, quantity, next) => {
    if (type === 'student' && quantity > 10) {
        return 0.8;
    }
    return next(type, quantity);
};
// 军人条件进入
const seniorHandler = (type, quantity, next) => {
    if (type === 'senior' && quantity > 20) {
        return 0.7;
    }
    return next(type, quantity);
};
// 没有特殊条件时进入
const defaultHandler = (type, quantity, next) => {
    return 1;
};

// 定义一个函数,这个函数会生成一个处理器链
const createHandlerChain = (...handlers) => (type, quantity) => {
    let index = 0;
    const next = (type, quantity) => {
        if (index < handlers.length) {
            return handlers[index++](type, quantity, next);
        }
    };
    return next(type, quantity);
};

// 组合函数
const calculate = createHandlerChain(studentHandler, seniorHandler, defaultHandler);

calculate('student', 15)
; // 输出: 0.8

各类设计模式和最佳实践能运用到实际项目中(复制粘贴,嘿嘿),对我们一定有不小地进步!

🎉 希望本文能够帮助你解决优化大量的 if...else 语句的问题。如果你有任何疑问或者想进一步讨论相关话题,请随时告诉我。🚀✨