By Amap_tech team.
As software project code accumulates over time, system maintenance costs rise. This problem is common across all software teams. Continuously optimizing and improving code quality is a great way to improve system vitality. Also, as the popular saying among developers goes: Think more, codeless. Therefore, we need to think more when programming and strive to improve our coding skills. This way, we can produce more elegant, higher quality, and more efficient code.
This article will describe a set of coding rules relevant to Java functions. We hope to give some coding advice to Java programmers, which will help them produce code with the aforementioned highlights. The Amap data collection department has put these rules into practice to great effect.
The following snippet shows the symptom description.
Incomplete code
thisName != null && thisName.equals(name);
More complete code
(thisName == name) || (thisName != null && thisName.equals(name));
Refer to the following recommended solution.
Objects.equals(name, thisName);
The following snippet shows the symptom description.
!(list == null || list.isEmpty());
Refer to the following recommended solution.
import org.apache.commons.collections4.CollectionUtils;
CollectionUtils.isNotEmpty(list);
When the code of a function exceeds 80 lines, it is a large function and therefore needs to be split up.
Each code block must contain a comment to explain its functionality. If a comment line is present in front of the code block, it allows replacing the code with a function and names the function based on the comment. If a function has a descriptive name, it is easy to update directly without having to check how the internal code is implemented.
The following snippet shows the symptom description.
// 每日生活函数
public void liveDaily() {
// 吃饭
// 吃饭相关代码几十行
// 编码
// 编码相关代码几十行
// 睡觉
// 睡觉相关代码几十行
}
Refer to the following recommended solution.
// 每日生活函数
public void liveDaily() {
// 吃饭
eat();
// 编码
code();
// 睡觉
sleep();
}
// 吃饭函数
private void eat() {
// 吃饭相关代码
}
// 编码函数
private void code() {
// 编码相关代码
}
// 睡觉函数
private void sleep() {
// 睡觉相关代码
}
The following snippet shows the symptom description.
// 生活函数
public void live() {
while (isAlive) {
// 吃饭
eat();
// 编码
code();
// 睡觉
sleep();
}
}
Refer to the following recommended solution.
// 生活函数
public void live() {
while (isAlive) {
// 每日生活
liveDaily();
}
}
// 每日生活函数
private void liveDaily() {
// 吃饭
eat();
// 编码
code();
// 睡觉
sleep();
}
The following snippet shows the symptom description.
// 外出函数
public void goOut() {
// 判断是否周末
// 判断是否周末: 是周末则游玩
if (isWeekday()) {
// 游玩代码几十行
}
// 判断是否周末: 非周末则工作
else {
// 工作代码几十行
}
}
Refer to the following recommended solution.
// 外出函数
public void goOut() {
// 判断是否周末
// 判断是否周末: 是周末则游玩
if (isWeekday()) {
play();
}
// 判断是否周末: 非周末则工作
else {
work();
}
}
// 游玩函数
private void play() {
// 游玩代码几十行
}
// 工作函数
private void work() {
// 工作代码几十行
}
The following snippet shows the symptom description.
// 每日生活函数
public void liveDaily() {
// 吃饭
eat();
// 编码
code();
// 睡觉
// 睡觉相关代码几十行
}
As you see, the sleep code block is not at the same level as eat and code, which is rather strange. If you compare writing code with writing an essay, eat and code are then the main ideas of a paragraph, while sleep is a detailed paragraph. For the LiveDaily function, you only need to write the main process, that is, the idea of the paragraph.
Refer to the following recommended solution.
public void liveDaily() {
// 吃饭
eat();
// 编码
code();
// 睡觉
sleep();
}
// 睡觉
private void sleep() {
// 睡觉相关代码
}
The following snippet shows the symptom description.
// 禁用用户函数
public void disableUser() {
// 禁用黑名单用户
List<Long> userIdList = queryBlackUser();
for (Long userId : userIdList) {
User userUpdate = new User();
userUpdate.setId(userId);
userUpdate.setEnable(Boolean.FALSE);
userDAO.update(userUpdate);
}
// 禁用过期用户
userIdList = queryExpiredUser();
for (Long userId : userIdList) {
User userUpdate = new User();
userUpdate.setId(userId);
userUpdate.setEnable(Boolean.FALSE);
userDAO.update(userUpdate);
}
}
Refer to the following recommended solution.
// 禁用用户函数
public void disableUser() {
// 禁用黑名单用户
List<Long> userIdList = queryBlackUser();
for (Long userId : userIdList) {
disableUser(userId);
}
// 禁用过期用户
userIdList = queryExpiredUser();
for (Long userId : userIdList) {
disableUser(userId);
}
}
// 禁用用户函数
private void disableUser(Long userId) {
User userUpdate = new User();
userUpdate.setId(userId);
userUpdate.setEnable(Boolean.FALSE);
userDAO.update(userUpdate);
}
Encapsulate similar code as a function, with the differences controlled by function parameters.
The following snippet shows the symptom description.
// 通过工单函数
public void adoptOrder(Long orderId) {
Order orderUpdate = new Order();
orderUpdate.setId(orderId);
orderUpdate.setStatus(OrderStatus.ADOPTED);
orderUpdate.setAuditTime(new Date());
orderDAO.update(orderUpdate);
}
// 驳回工单函数
public void rejectOrder(Long orderId) {
Order orderUpdate = new Order();
orderUpdate.setId(orderId);
orderUpdate.setStatus(OrderStatus.REJECTED);
orderUpdate.setAuditTime(new Date());
orderDAO.update(orderUpdate);
}
Refer to the following recommended solution.
// 通过工单函数
public void adoptOrder(Long orderId) {
auditOrder(orderId, OrderStatus.ADOPTED);
}
// 驳回工单函数
public void rejectOrder(Long orderId) {
auditOrder(orderId, OrderStatus.REJECTED);
}
// 审核工单函数
private void auditOrder(Long orderId, OrderStatus orderStatus) {
Order orderUpdate = new Order();
orderUpdate.setId(orderId);
orderUpdate.setStatus(orderStatus);
orderUpdate.setAuditTime(new Date());
orderDAO.update(orderUpdate);
}
The following snippet shows the symptom description.
// 是否通过函数
public boolean isPassed(Long userId) {
// 获取通过阈值
double thisPassThreshold = PASS_THRESHOLD;
if (Objects.nonNull(passThreshold)) {
thisPassThreshold = passThreshold;
}
// 获取通过率
double passRate = getPassRate(userId);
// 判读是否通过
return passRate >= thisPassThreshold;
}
Refer to the following recommended solution.
// 是否通过函数
public boolean isPassed(Long userId) {
// 获取通过阈值
double thisPassThreshold = getPassThreshold();
// 获取通过率
double passRate = getPassRate(userId);
// 判读是否通过
return passRate >= thisPassThreshold;
}
// 获取通过阈值函数
private double getPassThreshold() {
if (Objects.nonNull(passThreshold)) {
return passThreshold;
}
return PASS_THRESHOLD;
}
The following snippet shows the symptom description.
// 发送审核员结算数据函数
public void sendAuditorSettleData() {
List<WorkerSettleData> settleDataList = auditTaskDAO.statAuditorSettleData();
for (WorkerSettleData settleData : settleDataList) {
WorkerPushData pushData = new WorkerPushData();
pushData.setId(settleData.getWorkerId());
pushData.setType(WorkerPushDataType.AUDITOR);
pushData.setData(settleData);
pushService.push(pushData);
}
}
// 发送验收员结算数据函数
public void sendCheckerSettleData() {
List<WorkerSettleData> settleDataList = auditTaskDAO.statCheckerSettleData();
for (WorkerSettleData settleData : settleDataList) {
WorkerPushData pushData = new WorkerPushData();
pushData.setId(settleData.getWorkerId());
pushData.setType(WorkerPushDataType.CHECKER);
pushData.setData(settleData);
pushService.push(pushData);
}
Refer to the following recommended solution.
// 发送审核员结算数据函数
public void sendAuditorSettleData() {
sendWorkerSettleData(WorkerPushDataType.AUDITOR, () -> auditTaskDAO.statAuditorSettleData());
}
// 发送验收员结算数据函数
public void sendCheckerSettleData() {
sendWorkerSettleData(WorkerPushDataType.CHECKER, () -> auditTaskDAO.statCheckerSettleData());
}
// 发送作业员结算数据函数
public void sendWorkerSettleData(WorkerPushDataType dataType, WorkerSettleDataProvider dataProvider) {
List<WorkerSettleData> settleDataList = dataProvider.statWorkerSettleData();
for (WorkerSettleData settleData : settleDataList) {
WorkerPushData pushData = new WorkerPushData();
pushData.setId(settleData.getWorkerId());
pushData.setType(dataType);
pushData.setData(settleData);
pushService.push(pushData);
}
}
// 作业员结算数据提供者接口
private interface WorkerSettleDataProvider {
// 统计作业员结算数据
public List<WorkerSettleData> statWorkerSettleData();
}
To achieve elegant functions, we recommend keeping the number of code levels between 1 and 4. Excessive indentation makes functions hard to read.
The following snippet shows the symptom description.
// 获取用户余额函数
public Double getUserBalance(Long userId) {
User user = getUser(userId);
if (Objects.nonNull(user)) {
UserAccount account = user.getAccount();
if (Objects.nonNull(account)) {
return account.getBalance();
}
}
return null;
}
Refer to the following recommended solution.
// 获取用户余额函数
public Double getUserBalance(Long userId) {
// 获取用户信息
User user = getUser(userId);
if (Objects.isNull(user)) {
return null;
}
// 获取用户账户
UserAccount account = user.getAccount();
if (Objects.isNull(account)) {
return null;
}
// 返回账户余额
return account.getBalance();
}
The following snippet shows the symptom description.
// 获取合计余额函数
public double getTotalBalance(List<User> userList) {
// 初始合计余额
double totalBalance = 0.0D;
// 依次累加余额
for (User user : userList) {
// 获取用户账户
UserAccount account = user.getAccount();
if (Objects.nonNull(account)) {
// 累加用户余额
Double balance = account.getBalance();
if (Objects.nonNull(balance)) {
totalBalance += balance;
}
}
}
// 返回合计余额
return totalBalance;
}
Refer to the following recommended solution.
// 获取合计余额函数
public double getTotalBalance(List<User> userList) {
// 初始合计余额
double totalBalance = 0.0D;
// 依次累加余额
for (User user : userList) {
// 获取用户账户
UserAccount account = user.getAccount();
if (Objects.isNull(account)) {
continue;
}
// 累加用户余额
Double balance = account.getBalance();
if (Objects.nonNull(balance)) {
totalBalance += balance;
}
}
// 返回合计余额
return totalBalance;
}
Notes
Other Method: In the loop body, call the getUserBalance function in case 1 to obtain the user balance and then calculate the cumulative balance.
In the loop body, we recommend using continue only once. If you want to use continue multiple times, we recommend that you encapsulate the loop body as a function.
For more information, see "Case 2: Encapsulate a complex conditional expression as a function" in the next section.
The following snippet shows the symptom description.
// 获取门票价格函数
public double getTicketPrice(Date currDate) {
if (Objects.nonNull(currDate) && currDate.after(DISCOUNT_BEGIN_DATE)
&& currDate.before(DISCOUNT_END_DATE)) {
return TICKET_PRICE * DISCOUNT_RATE;
}
return TICKET_PRICE;
}
Refer to the following recommended solution.
// 获取门票价格函数
public double getTicketPrice(Date currDate) {
if (isDiscountDate(currDate)) {
return TICKET_PRICE * DISCOUNT_RATE;
}
return TICKET_PRICE;
}
// 是否折扣日期函数
private static boolean isDiscountDate(Date currDate) {
return Objects.nonNull(currDate) &&
currDate.after(DISCOUNT_BEGIN_DATE)
&& currDate.before(DISCOUNT_END_DATE);
}
The following snippet shows the symptom description.
// 获取土豪用户列表
public List<User> getRichUserList(List<User> userList) {
// 初始土豪用户列表
List<User> richUserList = new ArrayList<>();
// 依次查找土豪用户
for (User user : userList) {
// 获取用户账户
UserAccount account = user.getAccount();
if (Objects.nonNull(account)) {
// 判断用户余额
Double balance = account.getBalance();
if (Objects.nonNull(balance) && balance.compareTo(RICH_THRESHOLD) >= 0) {
// 添加土豪用户
richUserList.add(user);
}
}
}
// 返回土豪用户列表
return richUserList;
}
Refer to the following recommended solution.
// 获取土豪用户列表
public List<User> getRichUserList(List<User> userList) {
// 初始土豪用户列表
List<User> richUserList = new ArrayList<>();
// 依次查找土豪用户
for (User user : userList) {
// 判断土豪用户
if (isRichUser(user)) {
// 添加土豪用户
richUserList.add(user);
}
}
// 返回土豪用户列表
return richUserList;
}
// 是否土豪用户
private boolean isRichUser(User user) {
// 获取用户账户
UserAccount account = user.getAccount();
if (Objects.isNull(account)) {
return false;
}
// 获取用户余额
Double balance = account.getBalance();
if (Objects.isNull(balance)) {
return false;
}
// 比较用户余额
return balance.compareTo(RICH_THRESHOLD) >= 0;
}
The preceding code may also be filtered by using streaming programming.
This section applies only to internal project code that you are familiar with. It demonstrates how to avoid unnecessary null pointer judgments. For third-party middleware and system APIs, null pointer judgments are necessary to ensure code robustness.
The following snippet shows the symptom description.
// 创建用户信息
User user = new User();
... // 赋值用户相关信息
createUser(user);
// 创建用户函数
private void createUser(User user){
// 判断用户为空
if(Objects.isNull(user)) {
return;
}
// 创建用户信息
userDAO.insert(user);
userRedis.save(user);
}
Refer to the following recommended solution.
// 创建用户信息
User user = new User();
... // 赋值用户相关信息
createUser(user);
// 创建用户函数
private void createUser(User user){
// 创建用户信息
userDAO.insert(user);
userRedis.save(user);
}
The following snippet shows the symptom description.
// 保存用户函数
public void saveUser(Long id, String name) {
// 构建用户信息
User user = buildUser(id, name);
if (Objects.isNull(user)) {
throw new BizRuntimeException("构建用户信息为空");
}
// 保存用户信息
userDAO.insert(user);
userRedis.save(user);
}
// 构建用户函数
private User buildUser(Long id, String name) {
User user = new User();
user.setId(id);
user.setName(name);
return user;
}
Refer to the following recommended solution.
// 保存用户函数
public void saveUser(Long id, String name) {
// 构建用户信息
User user = buildUser(id, name);
// 保存用户信息
userDAO.insert(user);
userRedis.save(user);
}
// 构建用户函数
private User buildUser(Long id, String name) {
User user = new User();
user.setId(id);
user.setName(name);
return user;
}
The following snippet shows the symptom description.
// 查询用户列表
List<UserDO> userList = userDAO.queryAll();
if (CollectionUtils.isEmpty(userList)) {
return;
}
// 转化用户列表
List<UserVO> userVoList = new ArrayList<>(userList.size());
for (UserDO user : userList) {
UserVO userVo = new UserVO();
userVo.setId(user.getId());
userVo.setName(user.getName());
userVoList.add(userVo);
}
// 依次处理用户
for (UserVO userVo : userVoList) {
// 判断用户为空
if (Objects.isNull(userVo)) {
continue;
}
// 处理相关逻辑
...
}
Refer to the following recommended solution.
// 查询用户列表
List<UserDO> userList = userDAO.queryAll();
if (CollectionUtils.isEmpty(userList)) {
return;
}
// 转化用户列表
List<UserVO> userVoList = new ArrayList<>(userList.size());
for (UserDO user : userList) {
UserVO userVo = new UserVO();
userVo.setId(user.getId());
userVo.setName(user.getName());
userVoList.add(userVo);
}
// 依次处理用户
for (UserVO userVo : userVoList) {
// 处理相关逻辑
...
}
MyBatis is an excellent persistent-layer framework and a database middleware component widely used in projects. By analyzing MyBatis source code, you may ensure that the list and data items returned by the query function are not null and null pointer judgments are unnecessary in the code.
The following snippet shows the symptom description.
This development idea is fine but too conservative.
// 查询用户函数
public List<UserVO> queryUser(Long id, String name) {
// 查询用户列表
List<UserDO> userList = userDAO.query(id, name);
if (Objects.isNull(userList)) {
return Collections.emptyList();
}
// 转化用户列表
List<UserVO> voList = new ArrayList<>(userList.size());
for (UserDO user : userList) {
// 判断对象为空
if (Objects.isNull(user)) {
continue;
}
// 添加用户信息
UserVO vo = new UserVO();
BeanUtils.copyProperties(user, vo);
voList.add(vo);
}
// 返回用户列表
return voList;
}
Refer to the following recommended solution.
// 查询用户函数
public List<UserVO> queryUser(Long id, String name) {
// 查询用户列表
List<UserDO> userList = userDAO.query(id, name);
// 转化用户列表
List<UserVO> voList = new ArrayList<>(userList.size());
for (UserDO user : userList) {
UserVO vo = new UserVO();
BeanUtils.copyProperties(user, vo);
voList.add(vo);
}
// 返回用户列表
return voList;
}
Alibaba Cloud Planet Engine - A Powerful Satellite Imaging and Remote Sensing Platform
2,605 posts | 747 followers
FollowAlibaba Clouder - April 29, 2020
Aliware - November 4, 2019
Aliware - April 10, 2020
Changyi - February 16, 2020
Alibaba Cloud Community - February 14, 2022
Alibaba Cloud Native - November 4, 2019
2,605 posts | 747 followers
FollowA cost-effective, efficient and easy-to-manage hybrid cloud storage solution.
Learn MoreAlibaba Cloud (in partnership with Whale Cloud) helps telcos build an all-in-one telecommunication and digital lifestyle platform based on DingTalk.
Learn MoreBuild superapps and corresponding ecosystems on a full-stack platform
Learn MoreProvides comprehensive quality assurance for the release of your apps.
Learn MoreMore Posts by Alibaba Clouder