0.问题概述

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

其中:

也强调了”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, a guard is a boolean expression 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 used to avoid errors during execution. A typical example is checking that a reference about to be processed is not null, which avoids nullpointer failures. Other uses include using a boolean field for idempotence (so subsequent calls are nops), as in the dispose pattern. The guard provides an early exit from a subroutine, and is a commonly used deviation from structured programming, removing one level of nesting and resulting in flatter code:[1] replacing if guard { ... } with if 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 概念

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

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

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

1.3.2 使用场景

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 {

    @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...");
    }

}

最后,进行策略的应用

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中,可以使用函数编程进行优化:

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

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

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

}
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 优势

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://blog.csdn.net/weixin_43238030/article/details/127536083

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_11069.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注