I read various exception handling articles, but I couldn't help it, so I broadened my perspective and considered it. Summarize from the viewpoint of "how to make use of exceptions in the system to improve quality".
Identify the characteristics of the exception.
--In principle, it occurs when the conditions required for processing are not met. --Exception classes can use inheritance to control catch targets by specialization and generalization. --Ignore unchecked exceptions --Data can be added --The exception class name gives some explanation (ex: NotFoundException) --Structured (exceptions can be nested)
The essence of an exception is in the relationship between the caller and the exception generator. It's just a function to notify the caller of a message. It is necessary to devise to track all the causes, such as remote programs, etc.
If you use it as it is
--The side that generated the exception can only notify the event that occurred. --The catcher of the exception only knows that the method failed --The method that occurred can be identified by the stack trace
Think about how to implement it so that the developer does not have to read the code as much as possible. The viewpoint of cause investigation is as follows.
--Bug or failure (generally missing information) --Separation (who is bad) --Identification of the location of occurrence --Information for reproduction (generally missing information)
Organize based on the following sample implementation. Some methods are omitted for the sake of brevity.
//Bank transfer class
class BankTransfer implements BankProcess {
//Remittance information
Account from; //Remittance source account
Account to; //Remittance account
long amount; //Remittance amount
//Transfer processing
@override
void process() throws BankException {
try {
from.withdrawal(amount); //Withdrawal processing
to.deposit(amount); //Deposit processing
log("transfer completed."); //Log output
} catch (TransferException e) {
//Remittance failed
log("transfer failed."); //Log output
rollback();
log("rollback completed.");
throw e;
}
}
}
//Account processing
abstract class Account {
int no; //account number
//Deposit processing
void deposit(long amount) {
try {
//Assumption that an exception will occur
deposit(amount);
} catch (ConnectionTimeoutException e) { //2.2.3 Carriage (who is bad)
//Connection failed and account management system not available
var error = new TransferException(e); //2.2 Structure with logical exceptions
error.setNo(this.no); //2.1 Utilization of additional information
throw error;
}
}
//Withdrawal processing
void withdrawal(long amount) {
try {
//Assumption that an exception will occur
withdrawal();
} catch (ConnectionTimeoutException e) { //2.2.3 Carriage (who is bad)
//Connection failed and account management system not available
var error = new TransferException(e); //2.2 Structure with logical exceptions
error.setNo(this.no); //2.1 Utilization of additional information
throw error;
}
}
//Abstraction because each account has a different bank
abstract void deposit(int no, long amount);
abstract void withdrawal(int no, long amount);
}
When an exception is thrown, the information in the method that implements the catch statement can be collected. Set the related data from the beginning of the method to the catch occurrence point as an exception. Collect relevant data to the last.
--Class field --Method parameter values --Evaluation value of control statements such as IF / For (what data, what conditions) --Stack trace (added by default)
Solved the following viewpoints.
--Identification of the location of occurrence --Information for reproduction
From the following viewpoints, we will provide the necessary information as a result of the cause investigation.
--Bug or obstacle --Separation (who is bad)
After that, we will use the easy-to-imagine "bank remittance processing" as an example and implement it using logical exceptions. Focus on the implementation part of exception handling.
The exception class name tells you that "processing failed", but the class name acts as the subject. The following exception classes show the inheritance tree.
RuntimeException Unchecked exception
└── BankException Banking failure
└── NameResolveException Name addressing process failed (irrelevant below)
└── TransferException Remittance processing failed
In order to judge it as a bug, it can be solved by making a rule for catching exceptions. To put it the other way around, it is important how you can pinpoint the expected exception. For that purpose, in the case of an abstract exception (SQLException, etc.), it is necessary to narrow down the exception instances to be processed by the additional information of the exception.
--Bug: Uncaught Exception --Failure: Logical exception (suggests an expected event)
In the cause investigation, the necessary things are isolated, but the characters who are generally suspected are as follows.
--User (maybe the operation is bad) --Operator (Isn't the configuration / operation bad?) --Other connected component systems (DB, other web services, etc.)
This problem can be identified by the physical exception (causing exception) caught. An example is shown below.
--Exceptions that occur at the point of contact with user / operator operations -(Resolved by validation) --Exceptions caused by improper configuration
You can freely notify the upper method. It is also good to set it as additional information in the logical exception, You can also create a new OperatorException class (a class that suggests an operation error) and increase the number of abstract exception classes. You can also write it in the message. (Because the connection failed, XX failed.)
As a result, a stack trace is added and the investigation result is completed. An example of the above sample will be described.
--Disability and certification (due to logical exception) --Due to a connection problem with an external service (an exception where the cause exception set as a logical exception occurs at the point of contact with another service) --Remittance source account number, remittance destination account number, amount (additional information) --The method that actually happened (stack trace)
The exception that occurs must be caught somewhere and somehow processed. The motive of this design concept is "to make use of exceptions in the system to improve quality", so I thought that the following rules were necessary.
There are few reasons to use "Exception" to catch all exceptions.
--To not stop the system --When the provided library etc. forces it (when it is not cool)
The cases that can be implemented are shown below.
--Methods that can throw exceptions and shut down the system --main function --Endpoint for homebrew backend processing --Event loop --A method that cannot be stopped because there is a subsequent process --Job-based execution loop --HTTP request control
Some things, such as web application servers, handle exceptions properly, so In that case, you don't have to catch everything. It is necessary to verify the behavior when WAS catches an exception.
If you catch all the exceptions shown in 3.1, you will also catch unexpected physical exceptions. Therefore, it is necessary to record a bug report to the developer in a cohesive form. The possible methods are as follows.
--Record in log file --Display on a web screen, etc. --Send to log collection tool or platform --Output to standard error
Logical exceptions (expected exceptions) and physical exceptions (unexpected exceptions) can be easily distinguished. Exception class generalization and specialization come to life here. The code example is shown below.
try {
var job = new BankTransfer(from, to);
job.process();
} catch (BankException e) {
//Handling of expected exceptions
} catch (RuntimeException e) {
//Handling unexpected exceptions
}
If you do not create your own exception class, you cannot determine whether it is a bug or a failure based on the name of the exception class that occurred. Also, it is not possible to obtain information for reproduction. Therefore, by realizing the following things, it is possible to discover information effective for identifying the cause at an early stage.
--Create your own exception class and design it in a structured manner --Wrap the cause exception in your own exception class and set the data to reproduce the exception --Handle only the expected exception class to determine bugs / failures --Set information for isolation from other components --Exceptions are caught by the principles mentioned above
Recommended Posts