烂怂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 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]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 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 {
@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 优势
最直接的好处就是可以让又臭又长的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代码优化方案的更多相关文章
- 【转载】C代码优化方案
C代码优化方案 1.选择合适的算法和数据结构2.使用尽量小的数据类型3.减少运算的强度 (1)查表(游戏程序员必修课) (2)求余运算 (3)平方运算 (4)用移位实现乘除法运算 (5)避免不必要的整 ...
- Java代码优化方案 J2ME内存优化
Java代码优化方案 J2ME内存优化 从几本书上,N个网站上整理的一些JAVA代码优化方案,最近的项目只有1M内存可用,必须很抠门了~J2ME项目更要注意的 避免内存溢出 l 不用的对象释放(置空) ...
- (转)C代码优化方案
C代码优化方案 原文地址:http://www.uml.org.cn/c++/200811103.asp 目录 C代码优化方案 1.选择合适的算法和数据结构 2.使用尽量小的数据类型 3.减少运算的强 ...
- 人人都能掌握的Java服务端性能优化方案
作为一个Java后端开发,我们写出的大部分代码都决定着用户的使用体验.如果我们的后端代码性能不好,那么用户在访问我们的网站时就要浪费一些时间等待服务器的响应.这就可能导致用户投诉甚至用户的流失. 关于 ...
- Android 下的EXIF
一.什么是Exif Exif(Exchangeable Image File 可交换图像文件)是一种图象文件格式,它的数据存储与JPEG格式是完全相同的.实际上Exif格式就是在JPEG格式头部插入了 ...
- 记录一次Jmeter性能测试
一.引言 之前有总结过如何写Java请求测试用例类,写完测试脚本调通之后,信心满满地以为我准备好可以开始性能测试了.结果在评审测试计划的时候,当即被项目组狠狠的扇了一耳光,各种不确定的点:性能指标不明 ...
- Java服务端性能优化
<Java程序性能优化>说性能优化包含五个层次:设计调优.代码调优.JVM调优.数据库调优.操作系统调优. 常用的几个代码优化方案: 使用单例 对于IO处理.数据库连接.配置文件解析加载等 ...
- 优雅的写好Vue项目代码 — 路由拆分、Vuex模块拆分、element按需加载
目录 路由的拆分 VUEX模块拆分 Element UI库按需加载的优雅写法 路由的拆分 项目较大路由较多时,路由拆分是一个不错的代码优化方案,按不同业务分为多个模块,结构清晰便于统一管理. requ ...
- Vue.js前后端同构方案之准备篇——代码优化
收录待用,修改转载已取得腾讯云授权 导语 目前Vue.js的火爆不亚于当初的React,本人对写代码有洁癖,代码也是艺术.此篇是准备篇,工欲善其事,必先利其器.我们先在代码层面进行优化,对我们完成整个 ...
- if-else代码优化的八种方案
前言 代码中如果if-else比较多,阅读起来比较困难,维护起来也比较困难,很容易出bug,接下来,本文将介绍优化if-else代码的八种方案. 优化方案一:提前return,去除不必要的else 如 ...
随机推荐
- RHCE服务---Web
网站需求:请给openlab搭建web网站 1.基于域名www.openlab.com可以访问网站内容为 welcome to openlab!!! 2.给该公司创建三个子界面分别显示学生信息,教学资 ...
- 001-ksum 求符合条件的 k 个数 1. Two Sum/15. 3Sum/18. 4Sum/
推荐阅读 000-从零开始的数据结构与算法 001-01-ksum 求符合条件的 k 个数 1. Two Sum/15. 3Sum/18. 4Sum/ 002-两数相加 add two numbers ...
- CSharp读写world文档数据
背景 在工作中需要对比数据,然后输出一份world文档的对比报告.这需要用C#来读写world文件. 用到的工具 NPOI NPOI 地址:NPOI NPOI版本:2.6.0 个人项目的运行时版本:. ...
- 【深入浅出 Yarn 架构与实现】5-2 Yarn 三种调度器
本篇文章将深入介绍 Yarn 三种调度器.Yarn 本身作为资源管理和调度服务,其中的资源调度模块更是重中之重.下面将介绍 Yarn 中实现的调度器功能,以及内部执行逻辑. 一.简介 Yarn 最主要 ...
- VUE百度地图API调用(手机端、PC端、微信通用)
百度地图API-示例中心: https://lbsyun.baidu.com/jsdemo.htm#aCreateMap 1.引入百度地图(此处用到的是V2.0版本) 1> 建立一个js文件,例 ...
- Linux磁盘LVM根目录扩容
LVM 的基本概念 物理卷 Physical Volume (PV):可以在上面建立卷组的媒介,可以是硬盘分区,也可以是硬盘本身或者回环文件(loopback file).物理卷包括一个特殊的 hea ...
- [操作系统/Linux]磁盘分区
0 基本概念1: 盘片/盘面/磁头/扇区/磁道/柱面 本小节摘自: 硬盘基本知识(磁头.磁道.扇区.柱面) - 博客园 一张磁盘并不是拿过来直接用,需要先分区. 磁盘本身有很多sector(扇区).c ...
- 四月十二号java基础知识
1.面向对象的编程思想是力图使在计算机语言中对事物的描述与现实世界中该事物的本来面目尽可能地一致.2.类(class)和对象(object)是面向对象程序设计方法中最核心的概念3.类是对某一事物的描述 ...
- Docker中Nginx搭建以及配置
docker nginx搭建 1 docker pull nginx docker pull nginx 2 启动nginx docker run --name nginx -p 80:80 -d n ...
- PyG 图神经网络依赖环境安装(Anaconda)
1.默认用户在Anaconda的虚拟环境中已安装Pytorch 2.打开anaconda prompt命令窗, activate "你的虚拟环境名称" 3.在激活后的虚拟环境下输入 ...