0.问题概述

代码可读性是衡量代码质量的重要标准,可读性也是可维护性、可扩展性的保证,因为代码是连接程序员和机器的中间桥梁,要对双边友好。Quora 上有一个帖子: “What are some of the most basic things every programmer should know?”

其中:

  • Code that’s hard to understand is hard to maintain.

  • Code that’s hard to maintain is next to useless.

也强调了"easy understand"代码的重要性。

写这篇文章的契机是在研读Apache ShenYu项目时,看到了很大一坨的if else语句,如下:

这里并非评论这段代码写法有问题,因为我还并没有深入到项目细节之中,可能这已经是多轮优化的结果嘞。

但是这个多层if else的形式引发了我的思考,因为我也曾在项目代码中引入过如此繁重的if else结构,并在Code Review中被指出了问题。从那以后,我对if else的最大容忍层数就是三层。

我把大量if else的场景按照深度和广度两个维度划分为两种情况:

  • 嵌套层级过深

  • 平铺范围太广

下面就讨论一下,当代码中存在大量这样结构的代码的时候,该如何优化?

1.解决方案

1.1 尽早返回

又称卫语句,即Guard Statement

WikiPedia:

In computer programming, aguardis abooleanexpressionthat must evaluate to true if the program execution is to continue in the branch in question.

Regardless of which programming language is used, aguard clause,guard code, orguard statement, is a check of integritypreconditionsused to avoid errors during execution. A typical example is checking that a reference about to be processed is not null, which avoids null-pointer failures. Other uses include using a boolean field foridempotence(so subsequent calls are nops), as in thedispose pattern. The guard provides anearly exitfrom asubroutine, and is a commonly used deviation fromstructured programming, removing one level of nesting and resulting in flatter code:[1]replacingif guard { ... }withif not guard: return; ....

实际应用:

if (CollectionUtils.isNotEmpty(list)) {// do something
} else {   return xxx;
}

使用尽早返回优化:

if (CollectionUtils.isEmpty(list)) {return xxx;
}// do something

可以看到,优化后的代码不仅节省了一个else语句,也能让后续的"do something"节省一层if else包裹,代码看起来更干净一些

结合这个例子再说一下我对卫语句的理解:

可以将“卫”理解为“门卫”,门卫的作用是检查过滤,只有符合条件的语句,才可以继续执行,否则直接劝返(return)。吐槽一下这种中文直译有些晦涩,未免有点“德先生赛先生”的意思了。。。

1.2 使用switch或三元运算符

可以利用语法知识,对if else进行简化,

例如,当if else满足一定条件时:

if (condition1) {doSomeThing1();
} else if (condition2) {doSomeThing2();
} else if (condition3) {doSomeThing3();
} else if (condition4) {doSomeThing4();
} else {doSomeThing5();
}...

可以使用switch case语法进行替换

或,

例如使用三元运算符进行赋值操作:

Integer num = obejct == null ? 1 : object.value();

1.3 策略模式

1.3.1 概念

策略模式是一种行为设计模式,即一个对象有一个确定的行为,在不同场景下,这些行为有不同的算法实现。

例如从内蒙通过公共交通去北京是一个确定的行为,在天上这种场景可以选择飞机,地上的场景可以选择火车~

策略模式一般包含三个要素:

  • 抽象策略(Abstract strategy):定义所谓的“确定的行为”,一般由接口或抽象类实现

  • 具体实现(Concrete strategy):封装对应场景下的具体算法实现。

  • 上下文(Context):负责具体实现策略的管理并供对象使用。

1.3.2 使用场景

  • 一个接口或抽象类的各个子类都是为了解决相同的问题,区分这些子类的只有方法实现的不同。

  • 代码中使用大量if else或大面积switch case来选择具体的子实现类

1.3.3 实际应用

例如:

if ("man".equals(strategy)) {   // Perform related operations
} else if ("woman".equals(strategy)) {   // Perform operations related to women
} else if ("other".equals(strategy)) {   // Perform other operations
}

上面一段代码,每一个if分支完成的都是相同的操作,只是在不同的性别场景下,操作方法的实现不同,那么就可以使用策略模式进行优化:

首先,定义一个抽象策略接口:

public interface Strategy {void run() throws Exception;}

然后,进行不同策略的实现:

//Men's strategy implementation class
@Slf4j
public class ManStrategy implements Strategy {@Overridepublic void run() throws Exception {// Fast man's logiclog.debug("Execute the logic related to men...");}}//Women's strategy implementation class
@Slf4j
public class WomanStrategy implements Strategy {@Overridepublic void run() throws Exception {// Fast woman's logiclog.debug("Execute women related logic...");}}//Others' policy implementation class
@Slf4j
public class OtherStrategy implements Strategy {@Overridepublic void run() throws Exception {// Fast other logiclog.debug("Perform other related logic...");}}

最后,进行策略的应用:

public class StrategyTest {public static void main(String[] args) {try {Strategy strategy = initMap("man");strategy.run();} catch (Exception e) {e.printStackTrace();}}//Initialize the Map to obtain a gender policyprivate static Strategy initMap(String key) {//Use simple exampleHashMap<String, Strategy> map = new HashMap<>();map.put("man", new ManStrategy());map.put("woman", new WomanStrategy());map.put("other", new OtherStrategy());return map.get(key);}}

1.3.4 优劣势分析及优化

1.3.4.1 劣势

整体上来看,使用策略模式虽然剔除了大量的if else语句,但是也引入了更多的类文件,同时在Context中需要维护一个类似注册表的map对象,当增加策略实现时,容易忘记。

优化措施:

在Java中,可以使用函数式编程进行优化:

@Slf4j
public class StrategyTest {public static void main(String[] args) {//Use simple exampleHashMap<String, Strategy> map = new HashMap<>();map.put("man", () -> log.debug("Execute the logic related to men..."));map.put("woman", () -> log.debug("Execute women related logic..."));map.put("other", () -> log.debug("Execute logic related to others..."));try {map.get("woman").run();} catch (Exception e) {e.printStackTrace();}}
}

或者,使用枚举进行优化:

@Slf4j
public enum Strategy {//Man stateMAN(0) {@Overridevoid run() {//Perform related operationslog.debug("Execute the logic related to men");}},//Woman stateWOMAN(1) {@Overridevoid run() {//Perform operations related to womenlog.debug("Execute women related logic");}},//Other statusOTHER(2) {@Overridevoid run() {//Perform other related operationslog.debug("Perform other related logic");}};abstract void run();public int statusCode;Strategy(int statusCode) {this.statusCode = statusCode;}}
public static void main(String[] args) {try {//Simple use exampleString param = String.valueOf(Strategy.WOMAN);Strategy strategy = Strategy.valueOf(param);strategy.run();} catch (Exception e) {e.printStackTrace();}
}

除此以外,在客户端实际使用策略时,即对象进行方法的调用时,客户端必须知道这个策略的所有实现子类,并需要了解这些子类之间的不同以及各自的应用场景,这样客户端才能选择合适的策略实现“确定的行为”。

1.3.4.2 优势

  • 最直接的好处就是可以让又臭又长的if else代码块看起来更干净。

  • 面向对象的三大特点:封装、继承、多态,在策略模式中都能找到影子。面向接口编程,代码的可扩展性好

  • 代码的可测性好,Mock更方便,减少了分支判断,实现类只需要各自测试即可。

1.4 Optional

if else分支判断的很多情况都是进行非空条件的判断,Optional是Java8开始提供的新特性,使用这个语法特性,也可以减少代码中if else的数量,例如:

优化前:

String str = "Hello World!";if (str != null) {System.out.println(str);
} else {System.out.println("Null");
}

优化后:

Optional<String> optional = Optional.of("Hello World!");
optional.ifPresentOrElse(System.out::println, () -> System.out.println("Null"));

1.5 注册表

这种方式和策略模式有相似之处,但注册表更自由,不需要提炼接口,只需要将自定义实现在注册表中注册即可。

例如,优化前:

if (param.equals(value1)) {doAction1(someParams);
}else if (param.equals(value2)) {doAction2(someParams);
}else if (param.equals(value3)) {doAction3(someParams);
}

优化后:

//Generic here? For the convenience of demonstration, it can be replaced with the real type we need in actual development
Map<?, Function<?> action> actionMappings = new HashMap<>();
// When init
actionMappings.put(value1, (someParams) -> { doAction1(someParams)});
actionMappings.put(value2, (someParams) -> { doAction2(someParams)});
actionMappings.put(value3, (someParams) -> { doAction3(someParams)});// Omit null judgment
actionMappings.get(param).apply(someParams);

1.6 责任链模式

先来看一段代码:

public void handle(request) {if (handlerA.canHandle(request)) {handlerA.handleRequest(request);} else if (handlerB.canHandle(request)) {handlerB.handleRequest(request);} else if (handlerC.canHandle(request)) {handlerC.handleRequest(request);}
}

代码中也是存在一坨if else语句,但是和上述例子不同之处在于,if条件判断权在每个handler组件中,每一个handler的判断方式也可能不尽相同,相当灵活,同一个request可能同时满足多个if条件

解决方案就是参考开源组件中Filter或者Interceptor责任链机制,优化后代码:

public void handle(request) {handlerA.handleRequest(request);
}public abstract class Handler {protected Handler next;public abstract void handleRequest(Request request);public void setNext(Handler next) { this.next = next; }
}public class HandlerA extends Handler {public void handleRequest(Request request) {if (canHandle(request)) doHandle(request);else if (next != null) next.handleRequest(request);}
}

2.总结&思考

这篇文章主要介绍了代码中if else代码块泛滥时的治理措施,在实际应用时可根据具体场景选择合理的方案。

其实代码中存在大面积if else本无问题,用一句网络流行语来反驳就是:“你就说能不能用吧!”。但是作为有追求的工程师,我们要对项目以及代码负责,要及时的识别到代码中的坏味道,并持续重构优化。最后还想说一定要拥抱开源,多研读他人优秀代码,并临摹、思考、实践,日拱一卒,不期而至。

3.参考

  • https://programmer.ink/think/how-to-optimize-if-there-are-too-many-if-statements-in-java-code-of-series-17.html
  • WikiPedia
  • Quora

作者:京东零售 韩超

来源:京东云开发者社区

烂怂if-else代码优化方案 | 京东云技术团队相关推荐

  1. 基于AIGC的京东购物助手的技术方案设想 | 京东云技术团队

    灵感来源 随着AIGC的爆火,ChatGPT,GPT-4的发布,我作为一个算法工作者,深感AI发展的迅猛.最近,OpenAI的插件和联网功能陆续向用户公开,我也在第一时间试用了这些最新的功能.在Ope ...

  2. 万物云原生下的服务进化 | 京东云技术团队

    导读: 在万物云原生下的环境下,Java的市场份额也因耗资源.启动慢等缺点,导致在云原生环境里被放大而降低,通过这篇文章,读者可以更好地了解如何在云原生环境下通过升级相关版本和使用GraalVM打出原 ...

  3. 京东短网址高可用提升最佳实践 | 京东云技术团队

    作者:京东零售 郝彦军 什么是短网址? 短网址,是在长度上比较短的网址.简单来说就是帮您把冗长的URL地址缩短成8个字符以内的短网址. 当我们在腾讯.新浪发微博时,有时发很长的网址连接,但由于微博只限 ...

  4. 前端微服务无界实践 | 京东云技术团队

    一.前言 随着项目的发展,前端SPA应用的规模不断加大.业务代码耦合.编译慢,导致日常的维护难度日益增加.同时前端技术的发展迅猛,导致功能扩展吃力,重构成本高,稳定性低.因此前端微服务应运而生. 前端 ...

  5. 京东云专业安全服务介绍 | 京东云技术团队

    根据LogicMonitor发布的未来云服务趋势研究报告显示,到2020年,企业在各类云产品上的支出将高于其在一般IT服务成本六倍以上,与此同时,所有企业的工作量将有83%都在云上实现,各企业将继续加 ...

  6. 京东云高可用业务架构建设 | 京东云技术团队

    本文以 2022 年一个实际项目为基础,来演示在京东云上构建高可用业务的整个过程.公有云及私有云客户可通过使用京东云的弹性 IAAS.PAAS 服务,创建高可用.高弹性.高可扩展.高安全的云上业务环境 ...

  7. 性能测试监控指标及分析调优 | 京东云技术团队

    一.哪些因素会成为系统的瓶颈? 1.CPU,如果存在大量的计算,他们会长时间不间断的占用CPU资源,导致其他资源无法争夺到CPU而响应缓慢,从而带来系统性能问题,例如频繁的FullGC,以及多线程造成 ...

  8. 一种通用的业务监控触发方案设计 | 京东云技术团队

    一.背景 业务监控是指通过技术手段监控业务代码执行的最终结果或者状态是否符合预期,实现业务监控主要分成两步:一.在业务系统中选择节点发送消息触发业务监控:二.系统在接收到mq消息或者定时任务调度时,根 ...

  9. 京东小程序折叠屏适配探索 | 京东云技术团队

    前言 随着近年来手机行业的飞速发展,手机从功能机进入到智能机,手机屏幕占比也随着技术和系统的进步越来越大,特别是Android 10推出以后,折叠屏逐渐成为Android手机发展的趋势. 图 1 An ...

最新文章

  1. pyqt5 QtDesigner文件打开位置
  2. java设计模式-建造者模式
  3. Django 3.2.5博客开发教程:URL与视图函数
  4. 非常不错的MySQL优化的8条经验
  5. VS2010 C++下编译调试MongoDB“.NET研究”源码
  6. 布局new操作符引发的有关析构函数的探索与总结
  7. python降维可视化 自编码_如何使用自动编码器可视化降维? (Python | TensorFlow)...
  8. Ubuntu编译安装Keepalived
  9. OSChina 周六乱弹 ——从心动,到原谅
  10. 一个人是否靠谱,闭环很重要
  11. 带有Lowe’s算法的SIFT特征提取和匹配
  12. 递归--组合数计算--委员会问题--抽人问题
  13. C++ windy数
  14. C++面向对象程序设计习题1:分数相加
  15. matlab中imresize函数
  16. sql server2000 安装时出现“另一个安装程序实例已在运行”的解决办法
  17. 第一本书 第九章 使用对象吃货联盟
  18. 前端基础 es6、vue
  19. TI CC2540 USB CDC Serial Port驱动安装失败原因及解决方法
  20. SketchUp的自动化实战 (三)随机贴图

热门文章

  1. 第一章 计算机基础知识随堂练习,计算机应用基础随堂练习全解
  2. SunTorque分享-扭矩、力矩、转矩有什么区别?
  3. java弹弓类游戏_Android cocos2d 弹弓游戏 Catapult 源代码
  4. Android热修复技术总结
  5. 中国雾霾 China smog_ Sky dark from air pollution
  6. AI开发神器来了!支持CPU加速优化!!
  7. 机器学习理论学习:朴素贝叶斯
  8. oracle 如何创建、删除用户并授予权限
  9. 来了老弟,Layui树形菜单tree
  10. R语言(四) 自定义设置x轴时间刻度值