0.问题概述
代码可读性是衡量代码质量的重要标准,可读性也是可维护性、可扩展性的保证,因为代码是连接程序员和机器的中间桥梁,要对双边友好。Quora 上有一个帖子: “What are some of the most basic things every programmer should know?”
其中:
写这篇文章的契机是在研读Apache ShenYu项目时,看到了很大一坨的if else语句,如下:
这里并非评论这段代码写法有问题,因为我还并没有深入到项目细节之中,可能这已经是多轮优化的结果嘞。
但是这个多层if else的形式引发了我的思考,因为我也曾在项目代码中引入过如此繁重的if else结构,并在Code Review中被指出了问题。从那以后,我对if else的最大容忍层数就是三层。
我把大量if else的场景按照深度和广度两个维度划分为两种情况:
下面就讨论一下,当代码中存在大量这样结构的代码的时候,该如何优化?
1.解决方案
1.1 尽早返回
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 null–pointer 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 { ... }
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 (condition1) {
doSomeThing1();
} else if (condition2) {
doSomeThing2();
} else if (condition3) {
doSomeThing3();
} else if (condition4) {
doSomeThing4();
} else {
doSomeThing5();
}...
或,
Integer num = obejct == null ? 1 : object.value();
1.3 策略模式
1.3.1 概念
策略模式是一种行为设计模式,即一个对象有一个确定的行为,在不同场景下,这些行为有不同的算法实现。
例如从内蒙通过公共交通去北京是一个确定的行为,在天上这种场景可以选择飞机,地上的场景可以选择火车~
- 抽象策略(Abstract strategy):定义所谓的“确定的行为”,一般由接口或抽象类实现
- 具体实现(Concrete strategy):封装对应场景下的具体算法实现。
- 上下文(Context):负责具体实现策略的管理并供对象使用。
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对象,当增加策略实现时,容易忘记。
优化措施:
@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 优势
- 最直接的好处就是可以让又臭又长的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
原文地址:https://blog.csdn.net/weixin_43238030/article/details/127536083
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_11069.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!