takeaway

With the accumulation of software project code, the cost of system maintenance becomes higher and higher, which is a common problem faced by all software teams. Continuously optimize the code, improve the quality of the code, is one of the effective means to enhance the vitality of the system. In software systems thinking, there is a saying “Less coding, more thinking”, as well as a slang phrase “Think more, code Less”. Therefore, we think more and summarize more in coding, and strive to improve their coding level, so as to write more elegant, higher quality, more efficient code.

This article summarizes a set of coding rules related to Java functions, aiming to give the majority of Java programmers some coding suggestions, help you write more elegant, higher quality, more efficient code. This set of coding rules, through the practice in autonavi acquisition department, has achieved good results.

Use generic utility functions

Case a

Symptom Description:

Incomplete writing:

thisName ! =null && thisName.equals(name);

Copy the code

A more complete way of writing:

(thisName == name) || (thisName ! =null && thisName.equals(name));

Copy the code

Recommended scheme:

Objects.equals(name, thisName);

Copy the code

Case 2

Symptom Description:

! (list ==null || list.isEmpty());

Copy the code

Recommended scheme:

import org.apache.commons.collections4.CollectionUtils;
CollectionUtils.isNotEmpty(list);

Copy the code

The main benefits

  • Functional programming, reduced business code, logic at a glance;

  • General tool function, logic considerate, low probability of problems.

Splitting super-large functions

When a function has more than 80 lines, it is very large and needs to be split.

Example 1: Each block of code can be encapsulated as a letter

Each code block must have a comment that explains what the code block does.

If there is a comment in front of a block of code, it reminds you that the code can be replaced with a function, and that the function can be named based on the comment. If the function has a name that describes it properly, you don’t need to look at how the internal code is actually implemented.

Symptom Description:

// Daily life function
public void liveDaily() {
    / / to eat
    // Several dozen lines of code related to eating

    / / code
    // Encode relevant code dozens of lines

    / / sleep
    // Dozens of lines of sleep-related code
}

Copy the code

Recommended scheme:

// Daily life function
public void liveDaily() {
    / / to eat
    eat();

    / / code
    code();

    / / sleep
    sleep();
}

// Eat function
private void eat() {
    // Eat the relevant code
}

// Encode function
private void code() {
    // Encode the relevant code
}

// Sleep function
private void sleep() {
    // Sleep related code
}

Copy the code

Case 2: Each loop body can be encapsulated as a letter

Symptom Description:

// live function
public void live() {
    while (isAlive) {
        / / to eat
        eat();

        / / code
        code();

        / / sleepsleep(); }}Copy the code

Recommended scheme:

// live function
public void live() {
    while (isAlive) {
        // Daily lifeliveDaily(); }}// Daily life function
private void liveDaily() {
    / / to eat
    eat();

    / / code
    code();

    / / sleep
    sleep();
}

Copy the code

Case 3: Each conditional body can be encapsulated as a function

Symptom Description:

// External function
public void goOut() {
    // Check whether it is weekend
    // Check if it's a weekend
    if (isWeekday()) {
        // Play code dozens of lines
    }
    // Work on weekends
    else {
        // Dozens of lines of working code}}Copy the code

Recommended scheme:

// External function
public void goOut() {
    // Check whether it is weekend
    // Check if it's a weekend
    if (isWeekday()) {
        play();
    }
    // Work on weekends
    else{ work(); }}// Play the function
private void play() {
    // Play code dozens of lines
}

// Work function
private void work() {
    // Dozens of lines of working code
}

Copy the code

The main benefits

  • The shorter the function, the more simple the function, often a long life cycle;

  • The longer a function is, the more difficult it is to understand and maintain.

  • In excessively long functions, there are often duplicates of code that are hard to find.

The level of code blocks within a function should be as consistent as possible

Case a

Symptom Description:

// Daily life function
public void liveDaily() {
    / / to eat
    eat();

    / / code
    code();

    / / sleep
    // Dozens of lines of sleep-related code
}

Copy the code

Obviously, sleep is a block of code that’s not on the same level as eat and code. If you compare writing code to writing an essay, eat and code are the general ideas of paragraphs, while sleep is a detailed paragraph. On liveDaily, you only need to write the main flow (general idea of the paragraph).

Recommended scheme:

public void liveDaily() {
    / / to eat
    eat();

    / / code
    code();

    / / sleep
    sleep();
}

/ / sleep
private void sleep() {
    // Sleep related code
}

Copy the code

The main benefits

  • Function call shows the purpose, function to express logic, hierarchical easy to understand;

  • Non-hierarchical blocks of code in a function tend to be top-heavy.

Encapsulate the same function code as a function

Case 1: Encapsulate the same code as a function

Symptom Description:

// Disable user functions
public void disableUser() {
    // Disable blacklisted users
    List<Long> userIdList = queryBlackUser();
    for (Long userId : userIdList) {
        User userUpdate = new User();
        userUpdate.setId(userId);
        userUpdate.setEnable(Boolean.FALSE);
        userDAO.update(userUpdate);
    }

    // Disable expired users
    userIdList = queryExpiredUser();
    for (Long userId : userIdList) {
        User userUpdate = new User();
        userUpdate.setId(userId);
        userUpdate.setEnable(Boolean.FALSE); userDAO.update(userUpdate); }}Copy the code

Recommended scheme:

// Disable user functions
public void disableUser() {
    // Disable blacklisted users
    List<Long> userIdList = queryBlackUser();
    for (Long userId : userIdList) {
        disableUser(userId);
    }

    // Disable expired users
    userIdList = queryExpiredUser();
    for(Long userId : userIdList) { disableUser(userId); }}// Disable user functions
private void disableUser(Long userId) {
    User userUpdate = new User();
    userUpdate.setId(userId);
    userUpdate.setEnable(Boolean.FALSE);
    userDAO.update(userUpdate);
}
Copy the code

Case 2: Encapsulate similar code as a function

Encapsulate similar codes as functions, and differences are controlled by function parameters.

Symptom Description:

// through the work order function
public void adoptOrder(Long orderId) {
    Order orderUpdate = new Order();
    orderUpdate.setId(orderId);
    orderUpdate.setStatus(OrderStatus.ADOPTED);
    orderUpdate.setAuditTime(new Date());
    orderDAO.update(orderUpdate);
}

// reject the work order function
public void rejectOrder(Long orderId) {
    Order orderUpdate = new Order();
    orderUpdate.setId(orderId);
    orderUpdate.setStatus(OrderStatus.REJECTED);
    orderUpdate.setAuditTime(new Date());
    orderDAO.update(orderUpdate);
}

Copy the code

Recommended scheme:

// through the work order function
public void adoptOrder(Long orderId) {
    auditOrder(orderId, OrderStatus.ADOPTED);
}

// reject the work order function
public void rejectOrder(Long orderId) {
    auditOrder(orderId, OrderStatus.REJECTED);
}

// check the work order function
private void auditOrder(Long orderId, OrderStatus orderStatus) {
    Order orderUpdate = new Order();
    orderUpdate.setId(orderId);
    orderUpdate.setStatus(orderStatus);
    orderUpdate.setAuditTime(new Date());
    orderDAO.update(orderUpdate);
}

Copy the code

The main benefits

  • Encapsulate common functions, reduce the number of lines of code, improve code quality;

  • Encapsulating common functions makes business code more concise, readable and maintainable.

Encapsulate the function that gets parameter values

Case a

Symptom Description:

// Whether to pass the function
public boolean isPassed(Long userId) {
    // Get the pass threshold
    double thisPassThreshold = PASS_THRESHOLD;
    if (Objects.nonNull(passThreshold)) {
        thisPassThreshold = passThreshold;
    }

    // Get the pass rate
    double passRate = getPassRate(userId);

    // Check whether it passes
    return passRate >= thisPassThreshold;
}

Copy the code

Recommended scheme:

// Whether to pass the function
public boolean isPassed(Long userId) {
    // Get the pass threshold
    double thisPassThreshold = getPassThreshold();

    // Get the pass rate
    double passRate = getPassRate(userId);

    // Check whether it passes
    return passRate >= thisPassThreshold;
}

// Get the pass threshold function
private double getPassThreshold() {
    if (Objects.nonNull(passThreshold)) {
        return passThreshold;
    }
    return PASS_THRESHOLD;
}

Copy the code

The main benefits

  • Obtain parameter value from business function independent, make business logic clearer;

  • The encapsulated get parameter values are separate functions that can be reused in code.

Encapsulate the same logic through interface parameterization

Case a

Symptom Description:

// Send the auditor settlement data function
public void sendAuditorSettleData() {
    List<WorkerSettleData> settleDataList = auditTaskDAO.statAuditorSettleData();
    for (WorkerSettleData settleData : settleDataList) {
        WorkerPushData pushData = newWorkerPushData(); pushData.setId(settleData.getWorkerId()); pushData.setType(WorkerPushDataType.AUDITOR); pushData.setData(settleData); pushService.push(pushData); }}// Send the inspector settlement data function
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);
    }

Copy the code

Recommended scheme:

// Send the auditor settlement data function
public void sendAuditorSettleData() {
    sendWorkerSettleData(WorkerPushDataType.AUDITOR, () -> auditTaskDAO.statAuditorSettleData());
}

// Send the inspector settlement data function
public void sendCheckerSettleData() {
    sendWorkerSettleData(WorkerPushDataType.CHECKER, () -> auditTaskDAO.statCheckerSettleData());
}

// Send the operator settlement data function
public void sendWorkerSettleData(WorkerPushDataType dataType, WorkerSettleDataProvider dataProvider) {
    List<WorkerSettleData> settleDataList = dataProvider.statWorkerSettleData();
    for (WorkerSettleData settleData : settleDataList) {
        WorkerPushData pushData = newWorkerPushData(); pushData.setId(settleData.getWorkerId()); pushData.setType(dataType); pushData.setData(settleData); pushService.push(pushData); }}// Operator settlement data provider interface
private interface WorkerSettleDataProvider {
    // Count operator settlement data
    public List<WorkerSettleData> statWorkerSettleData();
}

Copy the code

The main benefits

  • The core logic is extracted from each business function, making the business code clearer and easier to maintain;

  • Avoid writing repetitive code multiple times. The more repetitive functions you can simplify, the better.

Reduce the function code hierarchy

If you want to make a function elegant, it is recommended that the code level be between 1 and 4. Too much indentation makes the function difficult to read.

Case 1: Use return to return the function early

Symptom Description:

// Get the user balance function
public Double getUserBalance(Long userId) {
    User user = getUser(userId);
    if (Objects.nonNull(user)) {
        UserAccount account = user.getAccount();
        if (Objects.nonNull(account)) {
            returnaccount.getBalance(); }}return null;
}

Copy the code

Recommended scheme:

// Get the user balance function
public Double getUserBalance(Long userId) {
    // Get user information
    User user = getUser(userId);
    if (Objects.isNull(user)) {
        return null;
    }

    // Get the user account
    UserAccount account = user.getAccount();
    if (Objects.isNull(account)) {
        return null;
    }

    // Return the account balance
    return account.getBalance();
}

Copy the code

Case 2: Use continue to end the loop prematurely

Symptom Description:

// Get the total balance function
public double getTotalBalance(List<User> userList) {
    // Initial total balance
    double totalBalance = 0.0D;

    // Add the balance in order
    for (User user : userList) {
        // Get the user account
        UserAccount account = user.getAccount();
        if (Objects.nonNull(account)) {
            // Add up the user balance
            Double balance = account.getBalance();
            if(Objects.nonNull(balance)) { totalBalance += balance; }}}// Return total balance
    return totalBalance;
}

Copy the code

Recommended scheme:

// Get the total balance function
public double getTotalBalance(List<User> userList) {
    // Initial total balance
    double totalBalance = 0.0D;

    // Add the balance in order
    for (User user : userList) {
        // Get the user account
        UserAccount account = user.getAccount();
        if (Objects.isNull(account)) {
            continue;
        }

        // Add up the user balance
        Double balance = account.getBalance();
        if(Objects.nonNull(balance)) { totalBalance += balance; }}// Return total balance
    return totalBalance;
}

Copy the code

Special instructions

Other ways: In the body of the loop, call the function getUserBalance from Case 1 (to get the user balance) and then add the balance.

It is recommended to use continue at most once in the body of the loop. If you need to use continue multiple times, it is recommended that you wrap the body of the loop as a function.

Case 3: Using conditional expression functions to reduce levels

Refer to the next chapter, “Case 2: Encapsulating complex conditional Expressions as functions”

The main benefits

  • Code levels are reduced, code indentation is reduced;

  • Module division clear, easy to read maintenance.

Encapsulate conditional expression functions

Case 1: Encapsulate a simple conditional expression as a function

Symptom Description:

// Get the ticket price function
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;
}

Copy the code

Recommended scheme:

// Get the ticket price function
public double getTicketPrice(Date currDate) {
    if (isDiscountDate(currDate)) {
        return TICKET_PRICE * DISCOUNT_RATE;
    }
    return TICKET_PRICE;
}

// Whether to discount the date function
private static boolean isDiscountDate(Date currDate) {
    return Objects.nonNull(currDate) && 
currDate.after(DISCOUNT_BEGIN_DATE)
        && currDate.before(DISCOUNT_END_DATE);
}

Copy the code

Case 2: Encapsulate complex conditional expressions as functions

Symptom Description:

// Get the list of rich users
public List<User> getRichUserList(List<User> userList) {
    // List of initial rich users
    List<User> richUserList = new ArrayList<>();

    // Search for rich users in sequence
    for (User user : userList) {
        // Get the user account
        UserAccount account = user.getAccount();
        if (Objects.nonNull(account)) {
            // Determine the user balance
            Double balance = account.getBalance();
            if (Objects.nonNull(balance) && balance.compareTo(RICH_THRESHOLD) >= 0) {
                // Add a local userrichUserList.add(user); }}}// Return to the list of tuhao users
    return richUserList;
}

Copy the code

Recommended scheme:

// Get the list of rich users
public List<User> getRichUserList(List<User> userList) {
    // List of initial rich users
    List<User> richUserList = new ArrayList<>();

    // Search for rich users in sequence
    for (User user : userList) {
        // Identify the rich user
        if (isRichUser(user)) {
            // Add a local userrichUserList.add(user); }}// Return to the list of tuhao users
    return richUserList;
}

// Whether to be a tuhao user
private boolean isRichUser(User user) {
    // Get the user account
    UserAccount account = user.getAccount();
    if (Objects.isNull(account)) {
        return false;
    }

    // Get the user balance
    Double balance = account.getBalance();
    if (Objects.isNull(balance)) {
        return false;
    }

    // Compare user balances
    return balance.compareTo(RICH_THRESHOLD) >= 0;
}

Copy the code

The above code can also be implemented with filtering using Stream programming.

The main benefits

  • The conditional expression is separated from the business function to make the business logic clearer.

  • Encapsulated conditional expressions are stand-alone functions that can be used repeatedly in code.

Try to avoid unnecessary null pointer judgments

This chapter is only applicable to the internal code of the project, and the code is their own understanding, so as to avoid unnecessary null pointer judgment. For third-party middleware and system interfaces, null pointer judgment must be done to ensure the robustness of the code.

Case 1: Call the function to ensure that the parameter is not empty, the function to be called as far as possible to avoid unnecessary null pointer judgment

Symptom Description:

// Create user information
User user = newUser(); .// Assign user information
createUser(user);

// Create a user function
private void createUser(User user){
    // Check that the user is empty
    if(Objects.isNull(user)) {
        return;
    }

    // Create user information
    userDAO.insert(user);
    userRedis.save(user);
}

Copy the code

Recommended scheme:

// Create user information
User user = newUser(); .// Assign user information
createUser(user);

// Create a user function
private void createUser(User user){
    // Create user information
    userDAO.insert(user);
    userRedis.save(user);
}

Copy the code

Case 2: The called function ensures that the return is not null, and the calling function tries to avoid unnecessary null pointer judgment

Symptom Description:

// Save the user function
public void saveUser(Long id, String name) {
    // Build user information
    User user = buildUser(id, name);
    if (Objects.isNull(user)) {
        throw new BizRuntimeException("Build user information is empty");
    }

    // Save the user information
    userDAO.insert(user);
    userRedis.save(user);
}

// Build the user function
private User buildUser(Long id, String name) {
    User user = new User();
    user.setId(id);
    user.setName(name);
    return user;
}

Copy the code

Recommended scheme:

// Save the user function
public void saveUser(Long id, String name) {
    // Build user information
    User user = buildUser(id, name);

    // Save the user information
    userDAO.insert(user);
    userRedis.save(user);
}

// Build the user function
private User buildUser(Long id, String name) {
    User user = new User();
    user.setId(id);
    user.setName(name);
    return user;
}

Copy the code

Case 3: The assignment logic ensures that the list item is not empty, and the processing logic tries to avoid unnecessary null pointer judgment

Symptom Description:

// Query the user list
List<UserDO> userList = userDAO.queryAll();
if (CollectionUtils.isEmpty(userList)) {
    return;
}

// Convert the user list
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);
}

// Process the users in turn
for (UserVO userVo : userVoList) {
    // Check that the user is empty
    if (Objects.isNull(userVo)) {
        continue;
    }

    // Handle the related logic. }Copy the code

Recommended scheme:

// Query the user list
List<UserDO> userList = userDAO.queryAll();
if (CollectionUtils.isEmpty(userList)) {
    return;
}

// Convert the user list
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);
}

// Process the users in turn
for (UserVO userVo : userVoList) {
    // Handle the related logic. }Copy the code

Case 4: MyBatis query function return list and data items are not empty, can not use the null pointer to judge

MyBatis is an excellent persistence layer framework and is one of the most widely used database middleware in projects. Through the analysis of MyBatis source code, the list and data items returned by the query function are not empty, in the code can not be null pointer judgment.

Symptom Description:

There’s nothing wrong with that, it’s just too conservative.

// Query user functions
public List<UserVO> queryUser(Long id, String name) {
    // Query the user list
    List<UserDO> userList = userDAO.query(id, name);
    if (Objects.isNull(userList)) {
        return Collections.emptyList();
    }

    // Convert the user list
    List<UserVO> voList = new ArrayList<>(userList.size());
    for (UserDO user : userList) {
        // Determine that the object is empty
        if (Objects.isNull(user)) {
            continue;
        }

        // Add user information
        UserVO vo = new UserVO();
        BeanUtils.copyProperties(user, vo);
        voList.add(vo);
    }

    // Returns the list of users
    return voList;
}

Copy the code

Recommended scheme:

// Query user functions
public List<UserVO> queryUser(Long id, String name) {
    // Query the user list
    List<UserDO> userList = userDAO.query(id, name);

    // Convert the user list
    List<UserVO> voList = new ArrayList<>(userList.size());
    for (UserDO user : userList) {
        UserVO vo = new UserVO();
        BeanUtils.copyProperties(user, vo);
        voList.add(vo);
    }

    // Returns the list of users
    return voList;
}

Copy the code

The main benefits

  • Avoid unnecessary null pointer judgment, streamline business code processing logic, improve the operation efficiency of business code;

  • These unnecessary null Pointers are basically Death code that never executes, and removal helps with code maintenance.