您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
烂怂if-else代码优化方案
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
烂怂if-else代码优化方案
jd****
2023-05-30
IP归属:北京
9080浏览
# 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语句,如下: ![image.png](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2022-10-31-21-27kVA627GrLwQrBH27z.png) 这里并非评论这段代码写法有问题,因为我还并没有深入到项目细节之中,可能这已经是多轮优化的结果嘞。 但是这个多层if else的形式引发了我的思考,因为我也曾在项目代码中引入过如此繁重的if else结构,并在Code Review中被指出了问题。从那以后,我对if else的最大容忍层数就是三层。 我把大量if else的场景按照深度和广度两个维度划分为两种情况: * 嵌套层级过深 * 平铺范围太广 下面就讨论一下,当代码中存在大量这样结构的代码的时候,该如何优化? # 1.解决方案 ## 1.1 尽早返回 又称卫语句,即Guard Statement > WikiPedia: > > In computer programming, a **guard** is a [boolean](https://en.wikipedia.org/wiki/Boolean_datatype) [expression](https://en.wikipedia.org/wiki/Expression_(programming)) that must evaluate to true if the program execution is to continue in the branch in question. > > Regardless of which programming language is used, a **guard clause**, **guard code**, or **guard statement**, is a check of integrity [preconditions](https://en.wikipedia.org/wiki/Precondition) used 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 for [idempotence](https://en.wikipedia.org/wiki/Idempotence) (so subsequent calls are nops), as in the [dispose pattern](https://en.wikipedia.org/wiki/Dispose_pattern). The guard provides an [early exit](https://en.wikipedia.org/wiki/Early_exit) from a [subroutine](https://en.wikipedia.org/wiki/Subroutine), and is a commonly used deviation from [structured programming](https://en.wikipedia.org/wiki/Structured_programming), removing one level of nesting and resulting in flatter code:[[1\]](https://en.wikipedia.org/wiki/Guard_(computer_science)#cite_note-beck-1) replacing `if guard { ... }` with `if not guard: return; ...`. 实际应用: ```java if (CollectionUtils.isNotEmpty(list)) { // do something } else { return xxx; } ``` 使用尽早返回优化: ```java if (CollectionUtils.isEmpty(list)) { return xxx; } // do something ``` 可以看到,优化后的代码不仅节省了一个else语句,也能让后续的"do something"节省一层if else包裹,代码看起来更干净一些 > 结合这个例子再说一下我对卫语句的理解: > > 可以将“卫”理解为“门卫”,门卫的作用是检查过滤,只有符合条件的语句,才可以继续执行,否则直接劝返(return)。吐槽一下这种中文直译有些晦涩,未免有点“德先生赛先生”的意思了。。。 ## 1.2 使用switch或三元运算符 可以利用语法知识,对if else进行简化, 例如,当if else满足一定条件时: ```java 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 实际应用 例如: ```java 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分支完成的都是相同的操作,只是在不同的性别场景下,操作方法的实现不同,那么就可以使用策略模式进行优化: 首先,定义一个抽象策略接口: ```java public interface Strategy { void run() throws Exception; } ``` 然后,进行不同策略的实现: ```java //Men's strategy implementation class @Slf4j public class ManStrategy implements Strategy { @Override public void run() throws Exception { // Fast man's logic log.debug("Execute the logic related to men..."); } } //Women's strategy implementation class @Slf4j public class WomanStrategy implements Strategy { @Override public void run() throws Exception { // Fast woman's logic log.debug("Execute women related logic..."); } } //Others' policy implementation class @Slf4j public class OtherStrategy implements Strategy { @Override public void run() throws Exception { // Fast other logic log.debug("Perform other related logic..."); } } ``` 最后,进行策略的应用: ```java 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 policy private static Strategy initMap(String key) { //Use simple example HashMap<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中,可以使用函数式编程进行优化: ```java @Slf4j public class StrategyTest { public static void main(String[] args) { //Use simple example HashMap<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(); } } } ``` 或者,使用枚举进行优化: ```java @Slf4j public enum Strategy { //Man state MAN(0) { @Override void run() { //Perform related operations log.debug("Execute the logic related to men"); } }, //Woman state WOMAN(1) { @Override void run() { //Perform operations related to women log.debug("Execute women related logic"); } }, //Other status OTHER(2) { @Override void run() { //Perform other related operations log.debug("Perform other related logic"); } }; abstract void run(); public int statusCode; Strategy(int statusCode) { this.statusCode = statusCode; } } ``` ```java public static void main(String[] args) { try { //Simple use example String 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的数量,例如: 优化前: ```java String str = "Hello World!"; if (str != null) { System.out.println(str); } else { System.out.println("Null"); } ``` 优化后: ```java Optional<String> optional = Optional.of("Hello World!"); optional.ifPresentOrElse(System.out::println, () -> System.out.println("Null")); ``` ## 1.5 注册表 这种方式和策略模式有相似之处,但注册表更自由,不需要提炼接口,只需要将自定义实现在注册表中注册即可。 例如,优化前: ```java if (param.equals(value1)) { doAction1(someParams); }else if (param.equals(value2)) { doAction2(someParams); }else if (param.equals(value3)) { doAction3(someParams); } ``` 优化后: ```java //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 责任链模式 先来看一段代码: ```java 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责任链机制,优化后代码: ```java 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
上一篇:如何基于G6进行双树流转绘制?
下一篇:基于AIGC的京东购物助手
jd****
文章数
1
阅读量
227
作者其他文章
01
烂怂if-else代码优化方案
0.问题概述代码可读性是衡量代码质量的重要标准,可读性也是可维护性、可扩展性的保证,因为代码是连接程序员和机器的中间桥梁,要对双边友好。Quora 上有一个帖子: “What are some of the most basic things every programmer should know?”其中:Code that’s hard to understand is hard to mai
jd****
文章数
1
阅读量
227
作者其他文章
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号