Business project: Design of exception class

Posted by nickvd on Sun, 30 Jan 2022 09:35:47 +0100

preface

The optimization of the company's old projects really has a long way to go.
Recently, I found that the old code always tries and catches, and then a log is printed in catch.

The company log system is very rubbish and the way of viewing is very low. This kind of printing is not much different from not printing;

What is the difference between an Exception and a RuntimeException?

Don't talk at length, but simply and clearly:

Exception: you need to catch exceptions manually or throw them out.

RuntimeException: we don't need to deal with this exception. It is completely taken over by the virtual machine.

In other words, if the RuntimeException is handed over to the jvm for processing, the thread will be terminated. We can generally use it for parameter verification. If the verification fails, there is no need to proceed.

Exception s can be caught manually, that is, even if exceptions are thrown, the thread can continue to run.

Design of business code

Design for RuntimeException exception:

First, we need to design a top-level interface to record the status code and exception information of business errors.

package com.sgy.common.data;

/**
 * Error code
 */
public interface Error {

    /**
     * Get error code
     *
     * @return
     */
    int getCode();

    /**
     * Get error message content
     *
     * @return
     */
    String getMessage();
}

Then, for the API interface, business scenario:

Assuming that the design of exception class is extracted separately into a public project, the following code ApiError class will need to be put into the business project.

package com.sgy.common.data;

import lombok.Getter;

@Getter
public enum ApiError implements Error {

    EEE(11, "System abnormality");

    ReturnCode(int code, String message) {
        this.code = code;
        this.message = message;
    }

    private int code;

    private String message;
}

ApiException interface exception

package com.sgy.common.exceptions;

import com.sgy.common.data.ApiError;
import lombok.Getter;

/**
 * Access layer exceptions, including parameter inspection exceptions, authorization exceptions, etc.
 */
public class ApiException extends RuntimeException {

    @Getter
    private int code;

    public ApiException(ApiError error) {
        super(error.getMessage());

        this.code = error.getCode();
    }

    public ApiException(ApiError error, Throwable cause) {
        super(cause);

        this.code = error.getCode();
    }

    public ApiException(ApiError error, String message) {
        super(message);

        this.code = error.getCode();
    }

    public ApiException(ApiError error, String message, Throwable cause) {
        super(message, cause);

        this.code = error.getCode();
    }
}

use

//Code in business item:
Optional.ofNullable(id).orElseThrow(() -> new ApiException(ApiError.EEE));

summary

That is, we need two things:

① Design a top-level interface Error, which abstracts two methods to obtain business Error code getCode and business Error information getMessage.

② Write a business exception class to implement RuntimeException; For example: ApiException extends RuntimeException.

③ For business use, inherit the Error interface, and then fill in the specific business Error status information.

In fact, in the third step, some companies do not do as enumerated above, but use interfaces

This class is also packaged in a separate project, rather than allowing the business project code to implement Error

package com.sgy.common.data;

import lombok.Getter;

/**
 * Access layer error
 */
@Getter
public class ApiError implements Error {

    private int code;

    private String message;

    public ApiError(int code, String message) {
        if (code < 1 || code > 999) {
            throw new IllegalArgumentException(String.format("ApiError Error code[%d]Out of range (1)~99)", code));
        }
        this.code = code;
        this.message = message;
    }

    public static ApiError with(int code, String message) {
        return new ApiError(code, message);
    }
}

use:

package com.misssad.common;

import com.sgy.common.data.ApiError;

/**
 * Define API errors, mainly parameter errors, authentication errors, etc. most of the time, it is enough to use a few defined by ApiErrors.
 */
@SuppressWarnings("checkstyle:interfaceistype")
public interface SgyApiErrors {

    // Custom API error code
    //ApiError XXX_ERROR = ApiError.with(10, "XXXX");
    /**
     * Consumable package name is empty
     */
    ApiError NAME_IS_NULL = ApiError.with(1, "Name is empty");
}

The design of Exception is similar. However, exceptions need to be captured manually or run up.
In the actual business project, I think it should be used less. The business code is to deal with the problem yourself.
Only the code of tool class and framework class is used more in these scenarios.