zoukankan      html  css  js  c++  java
  • 论减少代码中return语句的骚操作

    一.写作背景

    最近组内在推行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数量的检测,哈哈。

      

  • 相关阅读:
    jPlayer
    nodemon
    微信
    防盗链
    ES2015 (ES6)
    静态资源
    WebP
    Retina
    ui-grid
    React入门2
  • 原文地址:https://www.cnblogs.com/-beyond/p/13831474.html
Copyright © 2011-2022 走看看