返回首页

代码质量 / 重构 / 设计模式

还在写这么糟糕的 if-else 吗?试试优化一下它

深层嵌套的 if-else 是可读性杀手。本文记录几个把 if-else 拆干净的实用技巧:提前 return、卫语句、以及策略模式的落地。

左侧珊瑚橙缠结的乱线穿过蓝色过滤梳,变成右侧一根带节点的干净墨蓝线

最恶心的 if-else 示例

如果抛弃业务逻辑,看下面这样一段代码,已经足够恶心了。这还是消除了业务逻辑后的简化版,如果加上真实业务,可读性将更差:

if condition1:
    if sub_condition1:
        if sub_sub_condition1:
            if sub_sub_sub_condition1:
                do_extreme_thing1()
            elif sub_sub_sub_condition2:
                do_extreme_thing2()
            else:
                do_extreme_thing3()
        elif sub_sub_condition2:
            if sub_sub_sub_condition3:
                do_extreme_thing4()
            else:
                do_extreme_thing5()
        else:
            do_extreme_thing6()
    elif sub_condition2:
        if sub_sub_condition3:
            if sub_sub_sub_condition4:
                do_extreme_thing7()
            else:
                do_extreme_thing8()
        else:
            do_extreme_thing9()
    else:
        do_something_else1()
elif condition2:
    if sub_condition3:
        if sub_sub_condition4:
            if sub_sub_sub_condition5:
                do_extreme_thing10()
            else:
                do_extreme_thing11()
        else:
            do_extreme_thing12()
    else:
        do_something_else2()
else:
    if sub_condition4:
        if sub_sub_condition5:
            if sub_sub_sub_condition6:
                do_extreme_thing13()
            else:
                do_extreme_thing14()
        else:
            do_extreme_thing15()
    else:
        handle_default()

之前的代码运行还算可以,一直没出什么错误,说明在某个阶段是合格的。但后来需要改进、增加功能,拿到手后直接头大。

一些技巧

能用 if 就不用 if-else

看下面这段代码:

public void performOperation(int input) {
    if (input > 5) {
        // do something
    } else {
        // do something else
    }
}

提前 return 后:

public void performOperation(int input) {
    if (input > 5) {
        // do something
        return;
    }
    // do something else
}

这里的关键动作是”卫语句(Guard Clause)“:当满足特定条件时立刻结束当前方法,让主干逻辑保持在最外层缩进。

它带来的好处:

  • 降层级——原本 if-else 里 else 那段代码要缩进 1 级,现在回到方法顶层
  • 改突出——if 里那段其实是”提前退出的异常/边缘 case”,提前 return 让读者看到”下面才是核心逻辑”
  • 易扩展——将来再来一个前置校验,继续在开头加一段 if (...) return; 即可,不用改动主逻辑的缩进

嵌套 3 层以上的 if-else,几乎总能用卫语句先拉平至少一层

使用策略模式

假设我们要对不同类型的文件做不同处理。最朴素的写法:

public String processFile(File file, String fileType) {
    if ("json".equals(fileType)) {
        // 处理 JSON
        return handleJson(file);
    } else if ("xml".equals(fileType)) {
        // 处理 XML
        return handleXml(file);
    }
    return null;
}

当前的写法可读性还可以,但保不齐后续会新增其他类型——txtyamlcsv……如果新增这些功能,最简单的情形就是继续增加 if-else 分支。但这违反了开闭原则,并且维护性、可读性、整洁性都会下降。

改用策略模式:

步骤 1:定义抽象策略接口

public interface FileStrategy {
    String doOperation(File file);
}

步骤 2:定义各种具体策略

public class JsonFileStrategy implements FileStrategy {
    @Override
    public String doOperation(File file) {
        // ... 处理 JSON
        return "...";
    }
}

public class XmlFileStrategy implements FileStrategy {
    @Override
    public String doOperation(File file) {
        // ... 处理 XML
        return "...";
    }
}

public class TxtFileStrategy implements FileStrategy {
    @Override
    public String doOperation(File file) {
        // ... 处理 TXT
        return "...";
    }
}

步骤 3:上下文对象负责调度

class FileContext {
    private final FileStrategy strategy;

    public FileContext(FileStrategy strategy) {
        this.strategy = strategy;
    }

    public String executeStrategy(File file) {
        return strategy.doOperation(file);
    }
}

步骤 4:用工厂 + Map 做类型到策略的映射

public class StrategyFactory {

    private static final StrategyFactory INSTANCE = new StrategyFactory();
    private static final Map<String, FileStrategy> strategyMap = new HashMap<>();

    static {
        strategyMap.put("txt",  new TxtFileStrategy());
        strategyMap.put("xml",  new XmlFileStrategy());
        strategyMap.put("json", new JsonFileStrategy());
    }

    private StrategyFactory() {}

    public FileStrategy create(String fileType) {
        return strategyMap.get(fileType);
    }

    public static StrategyFactory getInstance() {
        return INSTANCE;
    }
}

调用侧变成:

FileStrategy strategy = StrategyFactory.getInstance().create(fileType);
String result = new FileContext(strategy).executeStrategy(file);

代价与收益:

  • ❌ 代码量增加,需要多写几个类
  • ❌ 认知门槛提高:读者要理解策略模式的调度关系
  • ✅ 新增文件类型只需要加一个 Strategy 类,再在工厂 Map 注册一行,主流程完全不改
  • ✅ 每个策略独立可测、可复用、可组合
  • ✅ 编译期就能拿到所有策略的类型信息,IDE 跳转、重构更好用

判断什么时候值得:如果分支数量 > 3 且未来还会继续增长,或者每个分支的实现细节较重(不只是 3 行),用策略模式是划算的。只有 2-3 个分支且逻辑简单,老老实实写 if-else 更可读。

总结

处理复杂 if-else 的三个层次:

  1. 能提前 return 就 return —— 用卫语句把边缘 case 前置,拉平主逻辑
  2. 能用查表就用查表 —— 简单的类型→值映射,直接用 Map / dict / switch,不写 if-else 链
  3. 多分支且分支重的场景用策略模式 —— 把”选择哪个分支”和”分支里做什么”分离

代码质量的一个朴素判断:一段代码从上往下读,能否像读散文一样顺畅。三层以上的嵌套 if-else 就打断了这种顺畅感,这时任何一种拆分都值得。

参考文章