掘金 后端 ( ) • 2024-03-31 14:17
1、概述

相同的业务需求不同层级的程序员实现方式不一样,经验稍微欠缺一点的新手程序员,可能单纯的实现功能,经验丰富的程序员,开发的代码可能会具有很好的扩展性、易读性、健壮性。相信很多小伙伴在工作团队中,有时候会一起code review,互相review代码,其实review代码时大家保持开放包容心态,是一种团队进度的方式。 今天分享的内容主要帮助大家从代码规范的角度,梳理出快速提升代码质量的建议,学完之后可以帮助大家在团队code review时,提供建议,帮大家写出高质量代码。

2、什么样的代码是高质量代码

如何评价一段代码的好与坏,其实是有一定主观性的,不同人有不同的标准和看法,但是总的概括下来优秀的代码一般具有如下特点:

高质量代码特点.png

3、如何提高代码质量

这里主要代码规范角度,小伙伴们可以快速理解掌握,并快速使用。

3.1 代码命名

项目名、模块名、包名、类名、接口名、变量名、参数名等,都会涉及命名,良好的代码命名是程序员的基本素养,对代码可读性非常重要。

  • 命名原则 1、Java采用驼峰命名,代码命名要使用通俗易懂的词汇,不要采用生僻单词; 2、团队内部或者项目中风格要统一,例如查询类方法,要么都使用findByXXX方式,或者queryByXXX、getByXXX等,不要几种混用,风格保持一致; 3、命名长度:个人建议有时候为了易于理解,可以将命名适当长一些,例如:如下方法,一看就知道是上传照片到阿里云服务器,
public void uploadPhotoImageToAliyun(String userPhotoImageUri){}

可以利用上下文语义简化变量命名长度,如下用户实体类变量命名可以简化,更简洁

public class User {
  private String userName;
  private String userPassword;
  private String userGender;
}
public class User {
  private String name;
  private String password;
  private String gender;
}

4、抽象类通常带有Abstract前缀,接口命名和实现类命名,通常类似这样RoleService,实现类跟一个Impl,如RoleServiceImpl

  • 注释 1、良好的代码注释对于可读性很重要,虽然有小伙伴可能会觉得好的命名可以替代注释; 2、个人觉得注释很重要,注释可以起到代码分隔作用,代码块总结作用,文档作用; 3、部分程序设计核心关键点,可以通过注释帮助其他研发人员理解; 4、注释是否越多越好呢,然而并不是这样,太多注释反而让人迷惑,增加维护成本,代码变动之后也需要对注释进行修改。
3.2 代码风格

良好的代码风格,可以提升代码可读性,主要梳理以下几点:

良好的代码风格.png

3.3 实用代码技巧
  • 将代码分隔成多个单元 代码逻辑太长不易阅读,将代码分隔成多个小的方法单元,更好理解和复用,如下所示,用户注册接口,包含账号、手机号校验及用户保存操作
public Long registerUser(String Account, String mobile, String password){
    // 校验账号是否重复
    if(StringUtils.isNotBlank(Account)){
    User user = userService.getUserByName(Account);
    AssertUtils.isNull(user, "用户账号已存在,不能重复");
    }
    // 校验手机号是否重复
    if(StringUtils.isNotBlank(mobile)){
    User user = userService.getUserByMobile(mobile);
    AssertUtils.isNull(user, "手机号已存在,不能重复");
    }
    // 保存用户到DB
    return userService.insert(Account, mobile, password);
    }

重构之后的代码如下:

public Long registerUser(String Account, String mobile, String password){
        // 校验账号是否重复
        checkAccountIsExists(Account);
        // 校验手机号是否重复
        checkMobileIsExists(mobile);
        // 保存用户到DB
        return userService.insert(Account, mobile, password);
    }
    
    private void checkAccountIsExists(String Account){
        if(StringUtils.isNotBlank(Account)){
            User user = userService.getUserByName(Account);
            AssertUtils.isNull(user, "用户账号已存在,不能重复");
        }
    }
    private void checkMobileIsExists(String mobile){
        if(StringUtils.isNotBlank(mobile)){
            User user = userService.getUserByMobile(mobile);
            AssertUtils.isNull(user, "手机号已存在,不能重复");
        }
    }

  • 避免方法太多参数 方法太多参数影响代码可读性,当方法参数太多时可以采取将方法抽取为几个私有方法,如下所示:
public User getUser(String username, String telephone, String email);

// 拆分成多个函数
public User getUserByUsername(String username);
public User getUserByTelephone(String telephone);
public User getUserByEmail(String email);

也可以将参数封装为对象,通过抽取为对象对于C端项目还能更好兼容,如果是对外暴露的接口,可以避免新老接口兼容问题

public User getUser(String username, String telephone, String email);

// 重构后将方法入参封装为对象
public class SearchUserRequest{
   private String username;
   private String telephone;
   private String email;
}
public User getUser(SearchUserRequest searchUserReq重构后将方法入参封装为对象
  • 不要使用参数null及boolean来判断 使用参数非空和为空作为代码的if、else分支,以及boolean参数作为代码分支,这些都不建议,如果可以尽量拆分为多个细小的私有方法;当然也不是绝对的,实际情况具体分析;
  • ** 方法设计遵守单一职责** 方法设计不要追求大而全,尽量做到职责单一,粒度细,更易理解和复用,如下所示:
public boolean checkUserIfExisting(String telephone, String username, String email)  { 
  if (!StringUtils.isBlank(telephone)) {
    User user = userRepo.selectUserByTelephone(telephone);
    return user != null;
  }
  
  if (!StringUtils.isBlank(username)) {
    User user = userRepo.selectUserByUsername(username);
    return user != null;
  }
  
  if (!StringUtils.isBlank(email)) {
    User user = userRepo.selectUserByEmail(email);
    return user != null;
  }
  
  return false;
}

// 拆分成三个函数
public boolean checkUserIfExistingByTelephone(String telephone);
public boolean checkUserIfExistingByUsername(String username);
public boolean checkUserIfExistingByEmail(String email);
  • 避免嵌套逻辑太深 避免if else太多的方法,可以使用卫语句,将满足条件的结果提前返回,或者使用枚举、策略模式、switch case等; 对于for循环太深嵌套,可以使用continue、break、return等提前结束循环,或者优化代码逻辑。
  • 使用解释性变量 尽量不要使用魔法值,要使用常量来管理,代码中复杂的判断逻辑可以使用解释性变量,如下所示:
public double CalculateCircularArea(double radius) {
  return (3.1415) * radius * radius;
}

// 常量替代魔法数字
public static final Double PI = 3.1415;
public double CalculateCircularArea(double radius) {
  return PI * radius * radius;
}
if (date.after(SPRING_START) && date.before(SPRING_END)) {
  // ...
} else {
  // ...
}

// 引入解释性变量后逻辑更加清晰
boolean isSpring = date.after(SPRING_START)&&date.before(SPRING_END);
if (isSpring) {
  // ...
} else {
  // ...
} 

以上经验都是笔者,结合工作经验总结、查询网上资料汇总的服务接口优化方案,如果对您有帮助,请给个三连加关注支持,后续会定期持续化更新技术类知识和经验分享,感谢大家。 点击如下链接可以查看https://www.bilibili.com/video/BV1N1421D7ck/ 美丽的程序人生开通了个人的公众号,欢迎大家微信搜索美丽的程序人生,给个关注,后续会在公众号更新, 个人的GitHub地址:https://github.com/zhcyixin/zhc-java.git ,欢迎小伙伴克隆下载,如果对您有帮助麻烦给个stars。