谈谈业务代码中IF-ELSE实践优化


前言:当你身处简单和重复的业务代码工作时,就需要不断思考总结出方法论,这样才能让自己得到成长

IF-ELSE分支判断是在编写业务代码中不可避免的程序语句,能够帮助我们解决分支判断,但是当一个方法中业务稍微复杂IF-ELSE分支就会很多,可能出现各式各样的嵌套判断以及流程处理。

在这里插入图片描述
本文并不涉及最佳实践,只是聊聊我在日常工作的使用总结,总共六点,这六点不是递进的优化,不存在孰优孰劣重要的是适用于特定的业务代码流程,选择合适和简单的方式最重要。

1.最好避免分支判断

既然分支判断麻烦,那最好就避免分支判断。但是这个似乎过于理想,不过我们可以针对一些简单的判断逻辑避免分支判断,这里所说的避免不是没有分支判断,而是学会利用现有的工具避免在我们自己的业务代码中嵌入分支判断,比如下面的案例比较数值大小,那么完全可以利用JDK的Math工具类来处理。再者对于一些集合的操作也可以利用集合工具包来处理。

【普通】

 double result;
  if (value <= MIN_LIMIT) {
    
    
      result = MIN_LIMIT;
  } else {
    
    
      result = value;
  }

【优化】

double result = Math.max(MIN_LIMIT, value);
2.最小化条件作用域

最小化条件作用域,尽量提出公共处理代码,可以利用公共变量来处理相同逻辑代码
【普通】

  Result result = service.doWork();
  if (result.isSuccess()) {
    
    
      String message = "任务处理成功";
      smsService.sendMessage(user.getPhone(), message);
  } else {
    
    
      String message = "任务处理失败:" + result.getMessage();
      log.warn(message);
      smsService.sendMessage(user.getPhone(), message);
  }

【优化】

 String message;
 Result result = service.doWork();
 if (result.isSuccess()) {
    
    
     message = "任务处理成功";
 } else {
    
    
     message = "任务处理失败:" + result.getMessage();
     log.warn(message);
 }
 smsService.sendMessage(user.getPhone(), message);
3.条件筛选位置

条件筛选位置所涉及的不是代码的可扩展性和可阅读性方面,而是在IF-ELSE上的优化。如果存在多个条件的筛选判断,那么将能够过滤掉大部分数据的判断分支放在前面,这会很好的帮助提升代码处理效率。

提前过滤掉特殊情况,更关注核心业务逻辑

4.卫语句

卫语句,就是把复杂的条件表达式拆分成多个条件表达式。比如 多个 if-elseif-else 嵌套, 可以拆分成多个 if。如下面代码

【普通】

 public void test () {
    
    
     if (isWeekend()) {
    
    
         if (isRest()) {
    
    
             System.out.println("看书学习");
         } else {
    
    
             System.out.println("加班");
         }
     } else {
    
    
         //这里省略了周一到周五的分支判断
     }
 }

【优化】
说明:
这里涉及两个点:
1、通过设置多个IF替换掉多重嵌套的IF-ELSE,通过设置多个IF会使得代码看起来更易阅读,而且对于每个IF中的处理逻辑又可以通过代码抽取的方法单独创建方法,如下面的代码看起来更好

public Object test() {
    
    
    if (!isWeekend()) {
    
    
      return doSomething1();
    }
    if (isRest()) {
    
    
     return   doSomething2();
    }
    if (other()) {
    
    
     return   doSomething3();
    }
    return   doSomething4();
}

2、通过卫语句的方法,提前提前过滤掉特殊情况,让自己更专注于核心业务代码的实现。

public void test() {
    
    
    // 提前过滤掉`特殊情况`
    if (!isWeekend()) {
    
    
        System.out.println("XXX");
        return; // 提前return
    }
    //提前过滤掉`特殊情况`
    if (isRest()) {
    
    
        System.out.println("XXX");
        return; // 提前return
    }
    //更关注于 `核心业务`代码实现
     doSomething();
}
5.选择分支,优先使用使用switch语句而不是if-else语句

if-else语句,每个if条件语句都要加装计算,直到if条件语句为true为止。switch语句进行了跳转优化,Java中采用tableswitch或lookupswitch指令实现,对于多常量选择分支处理效率更高。经过试验证明:在每个分支出现概率相同的情况下,低于5个分支时if-else语句效率更高,高于5个分支时switch语句效率更高。
【普通】

if (i == 1) {
    
    
    ...; // 分支1
} else if (i == 2) {
    
    
    ...; // 分支2
} else if (i == ...) {
    
    
    ...; // 分支3
} else {
    
    
    ...; // 分支n
}

【优化】

switch (i) {
    
    
    case 1 :
        ... // 分支1
        break;
    case 2 :
        ... // 分支2
        break;
    case ... :
        ... // 分支n
        break;
    default :
        ... // 分支n+1
        break;
}
6.分支修改成三元运算符

对于判断结果返回值比较单一的分支比较简单的那么可以使用三元运算符来替换掉对应的分支。比如说比较数值,(a >= b) ? a : b,对于这种一行代码可以解决的逻辑,也可以合理的选用&& 和 || 运算符搭配使用
普通

String title;
if (isMember(phone)) {
    
    
    title = "会员";
} else {
    
    
    title = "访客";
}

优化

String title = isMember(phone) ? "会员" : "访客";

注意:对于包装类型的算术计算,需要注意避免拆包时的空指针问题。

7.利用Map数据结构+策略模式+工厂模式处理复杂业务IF-ELSE

在实际的业务代码中,经常会有一些根据某个Type类型做不同业务逻辑处理的情况,这种通常会关联到很多的分支判断。此外,一个很重要的点是这部分判断业务会有后期修改增加等需求,那么如何对这部分的业务代码做优化处理呢?一个比较不错的实践参考是通过Map数据结构+策略模式来处理。

对于映射关系的 if-else 语句,可以用Map来简化。此外,此规则同样适用于简化映射关系的 switch 语句,利用Map数据结构解决分支问题。

【普通】

 public static String getNameByType(String type) {
    
    
        switch (type) {
    
    
            case "A001":
                return "登录成功";
            case "B001":
                return "密码错误";
            case "C001":
                return "验证码错误";
            case "D001":
                return "短信发送失败";

        }
    }

【优化】

private static final Map<String, String> MAP = ImmutableMap.<String, String>builder()
            .put("A001", "登录成功")
            .put("B001", "密码错误")
            .put("C001", "验证码错误")
            .put("D001", "短信发送失败").build();


    public static void main(String[] args) {
    
    
        String type = "A001";
        String result = MAP.get(type);

    }

上面的代码中使用Map存储了不同类型的信息,但是上面的代码比较固定,一方面扩展的话需要修改Map数据,另一方面不适合业务复杂的处理。

针对这种问题,可以使用策略模式+工厂方法的方式来解决业务复杂且需要便于扩展的问题

详细的代码设计不再陈述,详情见下面的博文:
https://juejin.cn/post/6844903974525468680

概述:
1、提供策略接口
2、提供StrategyFactory工厂类负责将不同分支处理对象存储到Map容器中
3、利用Spring种提供的InitializingBean接口在每个业务处理类加载完成时通过StrategyFactory注入到容器中
4、 根据不同的业务通过获取不同的Bean的方式获取对象从而进行具体的业务处理

猜你喜欢

转载自blog.csdn.net/Octopus21/article/details/114412950