一.写作背景

最近组内在推行checkstyle代码规范的检测,关于checkstyle的介绍可以参考:https://checkstyle.sourceforge.io,

在按照checkstyle修改问题时,遇到几个很头疼的问题,最头疼就是checkstyle对function中return数量的限制,这里有两种限制:

  1.对于void返回值的function,return数量最多只允许有1个;

  2.对于非void返回值的function,return数量最多只允许个有3个;

根据上面这两个限制,在修改代码的过程中,真的是很难受,按照这个规则来写代码,会让代码变得更难理解(和人的思维方式有区别),所以我下面会简单介绍一下为啥会很头疼,然后介绍相关的骚操作,用来减少return的数量。

原文地址:https://www.cnblogs.com/-beyond/p/13831474.html

二.多个return的实例

通常项目中很多的service中都会进行组装流程,一个function中会包含多个步骤,每个步骤都会有阶段性的处理结果,并且需要根据阶段性的结果进行判断是否需要继续执行;如果不继续执行,那么就需要返回对应的错误码给上层。

比如下面这样例子

package cn.ganlixin;

/**
* @author ganlixin
* @create 2020-10-17
*/
public class CookService { private WaterManager waterManager;
private VegetableManager vegetableManager;
private FireManager fireManager;
private RiceManager riceManager; public Result cook(Param param) {
log.info("start cook check, param:{}", param); Result fireResult = fireManager.getFire(param);
if (fireResult != Result.SUCCESS) {
log.error("cook failed, no fire, because {}", fireResult);
return fireResult;
} Result waterResult = waterManager.getWater(param);
if (waterResult != Result.SUCCESS) {
log.error("cook failed, no water, because {}", waterResult);
return waterResult;
} Result riceResult = riceManager.getRice(param);
if (riceResult != Result.SUCCESS) {
log.error("cook failed, no rice, because {}", riceResult);
return riceResult;
} Result vegetableResult = vegetableManager.getVegetables(param);
if (vegetableResult != Result.SUCCESS) {
log.error("cook failed, no vegetables, because {}", vegetableResult);
return vegetableResult;
} log.info("steps checked pass success, start to do cook");
return doCook(param);
}
}

上面的例子中,return的数量已经由5个了,其实这个还不算多,有的大流程包含10多个return,修改这样的代码,就挺烦的。

对于上面这个代码块,说一下我的思路:

  1.上面的代码,每一步操作结果进行判断,如果不符合要求,及时终止流程,符合正常的思维;

  2.也是有可以优化的地方:return数量过多,在一定的程度上,可以认为是封装做得不好,或者说没有按照步骤的相关性单拎出来封装;比如说上面的代码中,可以将获取水、米、蔬菜的三个步骤提出来,封装一个获取食材的方法,在提出来的方法中再进行判断,这样return的数量减少了一些,同时代码也更加好理解。

三.常见的return优化方式

  下面介绍几种修改代码的方式,这些方式在初期接入checkstyle的时候,我就是按照这种方式来修改的,但是我并不推荐使用。

3.1 合理使用三元表达式

修改前的代码

public Result case1(Param param) {
Integer code= doSomeAction(param);
if (code == 0) {
return Result.SUCCESS;
} else {
return Result.FAIL;
}
}

  使用三元表达式来修改,看起来更加清爽了,如下:

public Result case1(Param param) {
Integer code = doSomeAction(param);
return code == 0 ? Result.SUCCESS : Result.FAIL;
}

  

3.2 合并相关操作

就像前面说的,将获取水、米、蔬菜的三个步骤封装为一个大的步骤(获取食材);

除此之外,下面还有一个例子,case2接收入参后进行了两次参数合法性检测,其实这两次合法性检测是可以合并的:

public Result case2(Param param) {
log.info("input param:{}", param);
if (param == null) {
log.info("param error");
return Result.PARAM_ERROR;
} if (!checkParamValid(param)) {
log.info("param error");
return Result.PARAM_ERROR;
} Integer code = doSomeAction(param);
return code == 0 ? Result.SUCCESS : Result.FAIL;
}

  将两次检测合并,修改后的代码

public Result case2(Param param) {
log.info("input param:{}", param);
if (param == null || !checkParamValid(param)) {
log.info("param error");
return Result.PARAM_ERROR;
} Integer code = doSomeAction(param);
return code == 0 ? Result.SUCCESS : Result.FAIL;
}

  点评:

  1.将相关的操作进行封装、合并,不仅会让代码更简洁,阅读代码也会更好理解;

  2.上面虽然将检测操作进行了简单的合并,但是也存在一些问题,如果检测参数的时候,需要返回那个字段或者属性不合法,那么上面的方式也就不推荐了。

3.3 使用变量保存if.else.的返回值

直接看下面的代码:

public Result case3(Param param) {
log.info("input param:{}", param);
// 省略参数校验 if (param.role == "admin") {
// ... return adminHandle(param);
} else {
// .. return userHandle(param);
}
}

  使用变量保存分支的处理结果,然后统一返回

public Result case3(Param param) {
log.info("input param:{}", param);
// 省略参数校验 Result res = Result.FAIL;
if (param.role == "admin") {
// ... res = adminHandle(param);
} else {
// .. res = userHandle(param);
} return res;
}

  点评:

  其实这种方式并不推荐,如果分支较少,并且每个分支的操作只有几行代码,这样修改也没啥关系;

  但是如果分支很多,并且每个分支的代码量很大,那么不论是写代码或者看代码,亦或者是后面维护代码,都需要小心res变量被修改错了或者忘记修改。(题外话:如果分支代码量太大,可以将分支中的操作封装一下)。

  

3.4 利用if的逆向逻辑

看一下下面代码:

public Result case4(Param param) {
log.info("input param:{}", param);
if (!checkParam()) {
log.info("参数错误");
return Result.PARAM_ERROR;
} if (!checkPermission(param.getUserId)) {
log.info("没有权限");
return Result.PERMISSION_DENY;
} return doAction(param);
}

  要想减少上面代码return,的确是有方法,修改之后如下:

public Result case4(Param param) {
log.info("input param:{}", param);
Result result = Result.FAIL;
if (checkParam()) {
if (checkPermission(param.getUserId)) {
return doAction(param);
} log.info("没有权限");
Result.PERMISSION_DENY;
} else { log.info("参数错误");
result = Result.PARAM_ERROR;
} return result;
}

  点评:

  上面这种写法,有的人看着感觉没啥,有的人看着就挺别扭。

  按照修改之前的写法,当发现参数错误是,立马返回结果,也就浏览4行代码;如果按照修改之后的代码,校验参数错误到返回结果,需要看15行左右(这只是一个简单地示例,正常业务流程中包含的代码会更多)。

四.总结

这篇博客,不是真的想举例子讲怎么去减少return数量,我觉得return数量多点也没关系,及时return也就及时结束,比较符合人的思维方式。

虽然可以使用各种骚操作去减少return的数量,比如使用if..else..去搞也是可以的,但是这样的话,一方面,从阅读代码的角度,流程更复杂了;另一方面,使用if..else去搞多层嵌套,每行的代码长度就会很多,阅读代码也不方便。

写这篇博客,笼统点说就是:

  1.做事情要多总结,映射到代码上就是多提炼、多抽象、多封装;

  2.不要为了规范而放弃一下东西,映射到代码上,就是代码可读性、代码简洁性很重要;

如果通不过规范,那么可以找上级多沟通沟通,如果理由正当且合理,那么规范也是可以跳过的,比如使用@SuppressWarnings("ReturnCount")来忽略return数量的检测,哈哈。

  

论减少代码中return语句的骚操作的更多相关文章

  1. Guava中这些Map的骚操作,让我的代码量减少了50%

    原创:微信公众号 码农参上,欢迎分享,转载请保留出处. Guava是google公司开发的一款Java类库扩展工具包,内含了丰富的API,涵盖了集合.缓存.并发.I/O等多个方面.使用这些API一方面 ...

  2. 如何减少代码中的if-else嵌套

    实际项目中,往往有大量的if-else语句进行各种逻辑校验,参数校验等等,大量的if-else,语句使代码变得臃肿且不好维护,本篇文章结合我自己的经验,就减少if-else语句给出以下几种方案,分别适 ...

  3. try-catch-finally中return语句的执行

    catch里return后还会执行finally吗??在java里,是的.但是值得注意的是,在存在try-catch-finally的方法中,return可能出现的位置有4个,在try中,在catch ...

  4. Python中的”黑魔法“与”骚操作“

    本文主要介绍Python的高级特性:列表推导式.迭代器和生成器,是面试中经常会被问到的特性.因为生成器实现了迭代器协议,可由列表推导式来生成,所有,这三个概念作为一章来介绍,是最便于大家理解的,现在看 ...

  5. C#中word导出功能骚操作

    马上过牛年了,先祝大家新年好,身体好,心情好!!! 年前最后写一篇之前项目开发的一个功能,自己根据系统业务,想到的一个解决办法,效率还是不错的,废话不多说,开整!!! 需求:企业填报自己的企业信息到系 ...

  6. 项目中常用的js骚操作

    //打开网址window.open("http://www.runoob.com"); //判断是否为url var url = $("#url").val() ...

  7. trycatch中return语句如何执行

    测试代码如下: package reviewTest; /** * @ClassName: ReturnTest * @Description: 测试return在trycatch中的执行 * @au ...

  8. FindBugs 入门——帮你减少代码中的bug数

    FindBugs 入门 FindBugs 作用 开发人员在开发了一部分代码后,可以使用FindBugs进行代码缺陷的检查.提高代码的质量,同时也可以减少测试人员给你报的bug数. 代码缺陷分类 根据缺 ...

  9. jQuery的ajax中return语句无法返回值

    今天在做一个新需求的时候,用到jQuery的ajax来返回一个查询结果: 但是调用这个方法的时候,data有数据,调用的地方获取到的却一直都是undefined,在网上搜索了一些资料,找到了问题所在, ...

随机推荐

  1. IEDA使用Tomcat后控制台中文出现乱码

    如下图所示,Intellij IDEA显示中文为乱码, 根据Intellij IDEA控制台输出,Tomcat  Log出现乱码,因此可以将问题定位到Tomcat上,具体解决方法: 第一步:打开Tom ...

  2. java中整型、浮点型、char型扩展

    怎么区分是什么进制? 二进制:0b开头,eg: int i = 0b10; 八进制:0开头,eg: int k = 010; 十进制: 十六进制:0x开头,eg: int j = 0x10; 浮点数类 ...

  3. 【Azure DevOps系列】使ASP.NET Core应用程序托管到Azure Web App Service

    使用Azure DevOps Project设置ASP.NET项目 我们需要先在Azure面板中创建一个Azure WebApp服务,此处步骤我将省略,然后点击部署中心如下图所示: 此处我选择的是Az ...

  4. SpringMVC执行流程源码分析

    SpringMVC执行流程源码分析 我们先来看张图片,帮助我们理解整个流程 然后我们开始来解析 首先SpringMVC基于Servlet来运行 那么我们首先来看HttpServletBean这个类 他 ...

  5. 通过调用标识符确定this

    一. 纲 this的性质 作用:表示函数执行时的环境 值:一个对象 特点:动态性 确定this的难度 JS语言的动态性: 函数的this在执行时才能确定 函数为一级公民 可作实参.返回值.数据赋值进行 ...

  6. oracle之二控制文件

    控制文件 3.1 控制文件的功能和特点:       1)定义数据库当前物理状态,不断在往controlfile写入[SCN等]       2)维护数据的一致性       3)是一个二进制文件   ...

  7. oracle之二实例管理及数据库的启动/关闭

    实例管理及数据库的启动/关闭   2.1 实例和参数文件 1.instance 功能:用于管理和访问database.instance在启动阶段读取初始化参数文件(init parameter fil ...

  8. 多图证明,Java到底是值传递还是引用传递?

    开篇先来曝答案,在 Java 语言中,本质只有值传递,而无引用传递,解释和证明详见正文. 说到值传递和引用传递我们不得不提到两个概念:值类型和引用类型. 1.值类型 通俗意义上来说,所谓的值类型指的就 ...

  9. Java面试必问之-JUC

    JUC:java.util.concurrent (Java并发编程工具类) 代码:D:\JAVA\Java_Learning\Elipse_Project\workspace200301EE\JUC ...

  10. Mysql数据分片技术(一)——初识表分区

    1. 为什么需要数据分片技术 2. 3种数据分片方式简述 3. 分片技术原理概述 4. 对单表分区的时机 1为什么需要数据分片技术 数据库产品的市场 在互联网行业内,绝大部分开发人员都会遇到数据表的性 ...