当前位置: 首页 > 图灵资讯 > 技术篇> 烂怂if-else代码优化方案 | 京东云技术团队

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

来源:图灵教育
时间:2023-06-01 09:50:54

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项目中,我看到了一大堆ifenyu else语句如下:

烂怂if-else代码优化方案 | 京东云技术团队_ide

这并不是说这个代码写作有问题,因为我还没有深入到项目的细节中,这可能是多轮优化的结果。

但是这个多层if else的形式引起了我的思考,因为我也在项目代码中引入了如此繁重的if else结构和Code Review中指出了问题。从那以后,我对ifview 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@Slf4jpublic 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@Slf4jpublic class WomanStrategy implements Strategy {    @Override    public void run() throws Exception {        // Fast woman's logic        log.debug("Execute women related logic...");    }}//Others' policy implementation class@Slf4jpublic class OtherStrategy implements Strategy {    @Override    public void run() throws Exception {        // Fast other logic        log.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 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优化:

@Slf4jpublic 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();        }    }}

或者用枚举进行优化:

@Slf4jpublic 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;    }}

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的数量:

优化前:

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 developmentMap<?, Function<?> action> actionMappings = new HashMap<>(); // When initactionMappings.put(value1, (someParams) -> { doaction1(someParams)});actionMappings.put(value2, (someParams) -> { doaction2(someParams)});actionMappings.put(value3, (someParams) -> { doaction3(someParams)}); // Omit null judgmentactionMappings.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

作者:京东零售 韩超

来源:京东云开发者社区: