 码农老张								  		后端
			  							2023-06-06
码农老张								  		后端
			  							2023-06-06
				大家好,我是田螺。
我们开发完需求,提测前,一般都需要代码评审。小伙伴们,你们知道代码评审,一般都有哪些军规嘛?今天田螺哥给你带来代码评审的18个军规。

其实,写代码的时候,没有必要写太多的注释,因为好的方法名、变量名,就是最好的注释。以下就是笔者总结的一些注释规范:
TODO,也要注释清楚//;如果注释代码块或者接口方法的,有多行/* **/以下就是一些添加注释的demo:
php复制代码/**
 * @author 田螺
 * @date 2023/04/22 5:20 PM
 * @desc 田螺的实现类,捡田螺、卖田螺 (更多干货,关注公众号:捡田螺的小男孩)
 */
public class TianLuoClass {
 
    /**
     * 这是卖田螺的两法,它将两个田螺的价格整数相加并返回结果。
     * 
     * @param x 第一个整数
     * @param y 第二个整数
     * @return 两个整数的和
     */
    public int sellTianLuo(int x, int y) {
        return x + y;
    }
}
日志是快速定位问题的好帮手,是撕逼和甩锅的利器!打印好日志非常重要。如果代码评审的时候,这些日志规范没遵守,就需要修改:
error、warn、info、debug四种,不要反手就是info哈userId,bizSeq等等,不方便问题排查
对于日志打印规范,我之前整理出一篇文章,大家可以看一下哈,挺有用的:
Java代码的命名应该清晰、简洁和易于理解。我们代码评审的时候,要注意是否有命名不规范,不清晰的代码。下面是一些命名规范的建议:
我们代码评审的时候,要注意参数是否都做了校验,如userId非空检查、金额范围检查、userName长度校验等等。一般我们在处理业务逻辑的时候,要遵循先检查、后处理的原则。
如果你的数据库字段userName设置为
varchar(16),对方传了一个32位的字符串过来,你不校验参数,插入数据库直接异常了。
很多bug都是因为没做参数校验造成的,这一军规,是代码评审重点关注的哈:

typescript复制代码if(object!=null){
   String name = object.getName();
}
如果你要遍历列表,也需要判空
less复制代码  if (CollectionUtils.isNotEmpty(tianLuolist)) {
        for (TianLuo temp : tianLuolist) {
            //do something
        }
    }

良好的异常处理可以确保代码的可靠性和可维护性。因此,异常处理也是代码评审的一项重要规范。以下是一些异常处理的建议:
Exception异常,而应该尽可能捕获特定的异常finally块中释放资源,或者使用try-with-resourcee.printStackTrace(),而是使用log打印。catch了异常,要打印出具体的exception,否则无法更好定位问题Throwable causeRuntimeException,或者直接抛出Exception\Throwable。大家有兴趣可以看下之前我的这篇文章哈:Java 异常处理的十个建议
代码评审的时候,关注一下,代码编写设计是否满足模块话,接口是否具有可扩展性
比如你的需求是酱紫:是用户添加或者修改员工时,需要刷脸。那你是反手提供一个员工管理的提交刷脸信息接口?还是先思考:提交刷脸是不是通用流程呢?比如转账或者一键贴现需要接入刷脸的话,你是否需要重新实现一个接口呢?还是当前按业务类型划分模块,复用这个接口就好,保留接口的可扩展性。
如果按模块划分的话,未来如果其他场景比如一键贴现接入刷脸的话,不用再搞一套新的接口,只需要新增枚举,然后复用刷脸通过流程接口,实现一键贴现刷脸的差异化即可。

ConcurrentHashMap是线性安全的,HashMap就是非线性安全的version控制,悲观锁一般用select …for updatesychronized ,reentrantlockRedis分布式锁或者走zookeeper至于分布式锁,大家可以看下我之前的这几篇文章哈
Test,如:CalculatorTest.test开头+ 测试的方法,如testAdd.75%.Redis异常.良好的代码格式,可以使代码更容易阅读和理解。下面是一些常见的代码格式化建议:
80个字符类变量、实例变量、构造函数、公共方法、私有方法等。代码评审的时候,要重点关注是否考虑到了接口的兼容性.因为很多bug都是因为修改了对外旧接口,但是却不做兼容导致的。关键这个问题多数是比较严重的,可能直接导致系统发版失败的。新手程序员很容易犯这个错误哦~

所以,如果你的需求是在原来接口上修改,尤其这个接口是对外提供服务的话,一定要考虑接口兼容。举个例子吧,比如dubbo接口,原本是只接收A,B参数,现在你加了一个参数C,就可以考虑这样处理:
javascript复制代码//老接口
void oldService(A,B){
  //兼容新接口,传个null代替C
  newService(A,B,null);
}
//新接口,暂时不能删掉老接口,需要做兼容。
void newService(A,B,C){
  ...
}
代码评审的时候,要关注程序逻辑是否清晰。比如,你的一个注册接口,有参数校验、判断用户是否已经注册、插入用户记录、发送注册成功通知等功能。如果你把所有所有功能代码塞到一个方法里面,程序逻辑就不清晰,主次不够分明,反例如下:
ini复制代码 public Response registerUser(String userName, String password, String email) {
        if (userName == null || StringUtils.isEmpty(userName)) {
          log.info("用户名不能为空!");
            throw new BizException();
        }
        if (password == null || password.length() < 6) {
            log.info("密码长度不能少于6位!");
            throw new BizException();
        }
        if (email == null || StringUtils.isEmpty(email) || !email.contains("@")) {
            log.info("邮箱格式不正确!");
            throw new BizException();
        }
        Response response = new Response();
        UserInfo userInfo = userService.queryUserInfoByUsername();
        if (Objects.nonNull(userInfo)) {
            response.setCode(0);
            response.setMsg("注册成功");
            return response;
        }
        UserInfo addUserInfo = new UserInfo();
        addUserInfo.setUserName(userName);
        addUserInfo.setPassword(password);
        addUserInfo.setEmail(email);
        userService.addUserInfo(addUserInfo);
        MessageDo messageDo = new MessageDo();
        messageDo.setUserName(userName);
        messageDo.setEmail(email);
        messageDo.setContent("注册成功");
        messageService.sendMsg(messageDo);
        response.setCode(0);
        response.setMsg("注册成功");
        return response;
    }
其实,以上这块代码,主次不够分明的点:参数校验就占registerUser方法很大一部分。正例可以划分主次,抽一下小函数,如下:
scss复制代码    public Response registerUser(String userName, String password, String email) {
        //检查参数
        checkRegisterParam(userName, password, email);
        //检查用户是否已经存在
        if (checkUserInfoExist(userName)) {
            Response response = new Response();
            response.setCode(0);
            response.setMsg("注册成功");
            return response;
        }
        //插入用户
        addUser(userName, password, email);
        sendMsgOfRegister(userName, email);
        //构造注册成功报文
        Response response = new Response();
        response.setCode(0);
        response.setMsg("注册成功");
        return response;
    }
    private void sendMsgOfRegister(String userName, String email) {
        MessageDo messageDo = new MessageDo();
        messageDo.setUserName(userName);
        messageDo.setEmail(email);
        messageDo.setContent("注册成功");
        messageService.sendMsg(messageDo);
    }
    private void addUser(String userName, String password, String email) {
        UserInfo addUserInfo = new UserInfo();
        addUserInfo.setUserName(userName);
        addUserInfo.setPassword(password);
        addUserInfo.setEmail(email);
        userService.addUserInfo(addUserInfo);
    }
    private boolean checkUserInfoExist(String userName) {
        UserInfo userInfo = userService.queryUserInfoByUsername();
        if (Objects.nonNull(userInfo)) {
            return true;
        }
        return false;
    }
    private void checkRegisterParam(String userName, String password, String email) {
        if (userName == null || StringUtils.isEmpty(userName)) {
            log.info("用户名不能为空!");
            throw new BizException();
        }
        if (password == null || password.length() < 6) {
            log.info("密码长度不能少于6位!");
            throw new BizException();
        }
        if (email == null || StringUtils.isEmpty(email) || !email.contains("@")) {
            log.info("邮箱格式不正确!");
            throw new BizException();
        } 
    }
代码评审,也非常有必要评审代码是否存在安全性问题。比如:
CSRF令牌,以防止未经授权的人员执行这些操作。其实我以前写过一篇文章,保证数据安全的10种方案,大家可以看看哈:保证接口数据安全的10种方案
@Transactional的声明式事务。因为 @Transactional有很多场景,可能导致事务不生效。  大家可以看下我的这篇文章哈: 美团二面:spring事务不生效的15种场景什么是幂等?
计算机科学中,幂等表示一次和多次请求某一个资源应该具有同样的副作用,或者说,多次请求所产生的影响与一次请求执行的影响效果相同。
代码评审的时候,要关注接口是否考虑幂等。比如开户接口,多次请求过来的时候,需要先查一下该客户是否已经开过户,如果已经开户成功,直接返回开户成功的报文。如果还没开户,就先开户,再返回开户成功的报文。这就是幂等处理。
一般情况有这几种幂等处理方案:
幂等要求有个唯一标记,比如数据库防重表的一个业务唯一键。同时强调多次请求和一次请求所产生影响是一样的。

大家如果对接口幂等有兴趣的话,可以看下我之前的这篇文章: 聊聊幂等设计
代码评审的时候,如果用数据库、Redis、RocketMq等的中间件时,我们需要关注这些中间件的一些注意事项哈。
比如数据库:
SQL时,如果条数不明确,是否加了limit限制限制SQL是否有监控比如Redis:
hgetall、smember之前我写过一篇文章,介绍Redis使用注意点的,大家可以看一下哈:使用Redis,你必须知道的21个注意要点
理解几个常见的代码坏味道,大家代码评审的时候,需要关注一些哈:
远程调用是代码评审重点关注的一栏,比如:
HTTPS 或 SSL/TLS。