特定场景下大量 if-else 优化思路

391 阅读5分钟

  如果代码中存在大量的 if-else 结构,不仅可读性降低,后期维护也很麻烦。在实际开发中,需要根据代码的复杂度、可读性和可扩展性,来确定最佳的优化方案。总之,适合自己的才是最好的。

  常见的 if-else 结构大致可以分为两种,顺序型和嵌套型,下文会根据实际场景进行分析,并提供针对性的优化思路。

1. 顺序结构优化

public void process(String x) {
    if ("A".equals(x)){
        /* 具体操作 A */
    }
    else if ("B".equals(x)){
        /* 具体操作 B */
    }
    else if ("C".equals(x)){
        /* 具体操作 C */
    }
}

场景分析:

  1. 基于单一变量的条件判断。
  2. 各分支条件没有依赖关系。
  3. 各分支下执行逻辑相对独立。

1.1. 单 if 结构

public void process(String x) {
    if ("A".equals(x)){
        /* 具体操作 A */
    }
    if ("B".equals(x)){
        /* 具体操作 B */
    }
    if ("C".equals(x)){
        /* 具体操作 C */ 
    }
}

优缺点:

  1. 减少了 else 关键字,直观感受更简洁。
  2. 由于条件互斥且只会满足其中一个,单 if 会造成多余的判断。比如 x 等于 A,在处理完分支 A 后,仍会继续判断是否等于 B 和 C。

实践建议:

如果条件互斥且判断次数较多,if-else 更有效率;

如果追求代码可读性,单 if 结构更简洁。

1.2. switch 模式

public void process(String x) {
    switch (x) {
        case "A": 
            /* 具体操作 A */
            break;
        case "B": 
            /* 具体操作 B */
            break;
        case "C": 
            /* 具体操作 C */
            break;
        default: break;
    }
}

优缺点:

  1. switch 结构清晰且高效,代码可维护性好。
  2. 适合匹配值为常量或枚举类型。
  3. 只能处理等值判断,复杂条件判断无法实现,如大于、小于。

实践建议:

适合等值判断,且匹配值为常量或枚举类型的特定场景。

1.3. Map+函数式编程(表驱动)

public static final Map<String, Function<String,String>> actionMap = new ConcurrentHashMap<>();
// 规则
public static final Function<String,String> actionA = param -> {return "A";};
public static final Function<String,String> actionB = param -> {return "B";};
public static final Function<String,String> actionC = param -> {return "C";};

// 初始化规则到 actionMap
static {
    actionMap.put("A", actionA);
    actionMap.put("B", actionB);
    actionMap.put("C", actionC);
}

public void process4(String x,String param ) {
    actionMap.get(x).apply(param);
}

优缺点:

  1. 表驱动模式将判断条件和处理逻辑解耦,通过 Map 表来组织规则,可以动态扩展和修改。
  2. 要求 JDK 版本 1.8 以上,且函数式编程带来一定的复杂度,可读性变差。
  3. 只能处理等值判断,复杂条件判断无法实现,如大于、小于。

实践建议:

适合规则较多、变动频繁的场景,如把 actionMap 的初始化独立出来,可以根据外部配置文件动态加载处理规则。

1.4. 工厂+策略模式

// 策略接口
public interface Strategy {
    void execute(String param);
}

// 策略实现 A
public class StrategyA implements Strategy {
    public void execute(String param) { /* 具体操作 A */ }
}
// 策略实现 B
public class StrategyB implements Strategy {
    public void execute(String param) { /* 具体操作 B */ }
}
// 策略实现 C
public class StrategyC implements Strategy {
    public void execute(String param) { /* 具体操作 C */ }
}

// 策略类型
public enum StrategyType {
    A,B,C;
}

// 策略工厂
public class StrategyFactory {
    private static final Map<StrategyType, Strategy> strategyMap = new ConcurrentHashMap<>();
    {
        strategyMap.put(StrategyType.A,new StrategyA());
        strategyMap.put(StrategyType.B,new StrategyB());
        strategyMap.put(StrategyType.C,new StrategyC());
    }
    public static Strategy getStrategy(StrategyType type) {
        return strategyMap.get(type);
    }
}

// 调用
StrategyFactory.getStrategy(StrategyType.A).execute(param);
	

优缺点:

  1. 每个策略可以独立实现,符合开放封闭原则,降低代码耦合度。
  2. 利用策略接口约束行为,有利于功能扩展方便,比如在接口新增一个 submit(String param)方法,子类分别实现就行了。
  3. 需要定义策略接口及其实现类,会增加文件数量,可能会增加系统复杂度。

实践建议:

适合复杂逻辑,扩展性高,尤其是需要动态选择不同行为的场景。如根据不同的会员类型来处理对应付费权益。

可结合依赖注入框架(如 Spring)来管理策略的实例化和调用。

2. 嵌套结构优化

// 嵌套 if-else 结构
public void process(String X, String Y, String Z) {
    if ("A".equals(X)) {
        if ("B".equals(Y)) {
            if ("C".equals(Z)) {
                 /* 具体操作 A && B && C*/
            } else {
                 /* 具体操作 !C */
            }
        } else {
             /* 具体操作 !B */
        }
    } else {
       /* 具体操作 !A */
    }
}

2.1. 卫语句

public void process(String X, String Y, String Z) {
    // 反向处理逻辑
    if(!"A".equals(X)){
        /* 具体操作 !A */
        return;
    }
    if(!"B".equals(Y)){
        /* 具体操作 !B */
        return;
    }
    if(!"C".equals(Z)){
        /* 具体操作 !C */
        return;
    }
    // 正向处理逻辑
    /* 具体操作 A && B && C*/
}

优缺点:

  1. 卫语句通过预先检查关键条件,在顶部排除不符合的条件,把多个嵌套 if-else 转为单 if 结构。
  2. 可能会在不同的条件分支中,多次使用 return 语句,增加控制流程的复杂性。

实践建议:

适合条件检查较多,且需要处理反向逻辑的特定场景。

2.2. 责任连模式

// 抽象处理器接口
public abstract class Processor {
    protected Processor nextProcessor;

    public Processor setNext(Processor nextProcessor) {
        this.nextProcessor = nextProcessor;
        return nextProcessor;
    }

    public abstract void process(String X, String Y, String Z);
}

// 具体处理器实现
public class AProcessor extends Processor {
    @Override
    public void process(String X, String Y, String Z) {
        if ("A".equals(X)) {
            if (nextProcessor != null) {
                nextProcessor.process(X, Y, Z);
            }
        } else {
            /* 具体操作 !A */
        }
    }
}

public class BProcessor extends Processor {
    @Override
    public void process(String X, String Y, String Z) {
        if ("B".equals(Y)) {
            if (nextProcessor != null) {
                nextProcessor.process(X, Y, Z);
            }
        } else {
            /* 具体操作 !B */
        }
    }
}

public class CProcessor extends Processor {
    @Override
    public void process(String X, String Y, String Z) {
        if ("C".equals(Z)) {
             /* 具体操作 A && B && C*/
        } else {
           /* 具体操作 !C */
        }
    }
}

// 构建责任链
Processor chain = new AProcessor();
chain.setNext(new BProcessor()).setNext(new CProcessor());
chain.process("X", "Y", "Z");

优缺点:

  1. 每个判断逻辑独立成一个处理类,降低耦合度。
  2. 每个条件都需要一个处理类,可能会导致文件数量增加,复杂的责任链容易引入调试困难。
  3. 可以根据需要,动态调整处理器的顺序和内容,易扩展。

实践建议:

适合按条件顺序执行的特定场景,请求按链式传递,直到找到符合条件的节点来处理。

3. 总结

  • 简单判断条件:使用 if-else 或单 if 更直接。
  • 枚举或常量判断:switch 结构更简洁。
  • 规则变动频繁或需要动态选择行为:表驱动(Map + 函数式编程)或策略模式更灵活。
  • 按顺序判断处理:责任链模式更适合。