Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing business logic in catch block [closed]

Tags:

java

I'm trying to understand the right way of using java catch blocks. Should I write business logic there or just suppress an error?

My question in some part related to this one. Check it out, please.

What I understand:

If it unchecked exception the best way is to write code in next way:

Integer n = null;
try {
    n = Integer.parseInt(reader.readLine());
}
catch(NumberFormatException e){
    log.error('Can't parse string');
}
if (n == null) {
    n = 0;
}

and avoid code like this one:

Integer n = null;
try {
    n = Integer.parseInt(reader.readLine());
}
catch(NumberFormatException ignored){
    n = 0;
}

My question:

But what it will be checked exception, and I work e.g. with a database wich throws exception NoSuchElement, when searching element was not found:

User user = null;

try {
    user = User.findById(userId);
} catch (NoSuchCategoryException e) {
    log.error('User {} doesn't exist.', userId);
    user = new User();
}
user.setUsername('someName');
etc...

So can I write code like listed above or should use the same pattern like in first my example?

like image 831
Vladyslav Sheruda Avatar asked Nov 30 '15 08:11

Vladyslav Sheruda


People also ask

Can catch block write business logic?

You can use any code you want on any catch block.

Is it good to write code in catch block?

Catch blocks should not be used for writing code logic. They should be used only for handling errors.

What do you write inside a catch block?

Place any code statements that might raise or throw an exception in a try block, and place statements used to handle the exception or exceptions in one or more catch blocks below the try block. Each catch block includes the exception type and can contain additional statements needed to handle that exception type.

Which block contains business logic where an exception might occur?

The block of code in which an exception may occur is enclosed in a try block. This block is also called “protected” or “guarded” code.


1 Answers

Catch blocks are regular control flow, there is no point in avoiding them.

However, don't catch the error in places that can't handle it yet. You may want to know that there was an input error in a place further away.

Manually signalling errors by replacing the result with null is very error prone (NullPointerException hooray) and you easily lose the meaning of the null. Manually adding all the control flow to propagate such a replacement value on a short path back to the caller is also not always great because you would get all this for free if you used the exception. The rule of thumb is: less code is cleaner.

The golden rule for exceptions is "catch late" (or more general "handle late"): In the place that contains the business logic that knows what to do. In some cases that means replacing a checked exception with an unchecked one so it can propagate through predefined interfaces that don't allow adding throws clauses.

Integer n = null;
try {
    n = Integer.parseInt(reader.readLine());
}
catch(NumberFormatException e){
    log.error('Can't parse string');
}
if (n == null) {
    n = 0;
}

This is the most problematic version. It's superficially avoiding the catch block and adds unneeded code & code branches. You should use what you have in the second example because it's way cleaner - That is, in case your business logic actually is to replace input errors with 0 and silently ignore them otherwise. It's most of the time not. Immediately returning control & information what happened back to the caller often is.

Not catching here might be right(er). Or catching and replacing the exception with regular API or a different Exception. What's right for you depends.

What the other question tells you is not to overuse exceptions in your own APIs. You can find a clean API / abstraction that signals the error without using exceptions. Not using Exceptions is especially useful when the place throwing the exception and the one that needs to handle them is conceptually far away (architecture layers etc). Exceptions as API aren't great in those cases because it leads to either a bunch of throws clauses that everybody has to have (which is adding dependencies on details https://en.wikipedia.org/wiki/Dependency_inversion_principle ) or code that needs to have knowledge of implementation details of other code, i.e. which unchecked exceptions are thrown somewhere else.

But if you contain exceptions within a reasonable perimeter they are absolutely fine to use and can make life easier. It's not against the rules to use all the capabilities of plain old java.

User user = null;
try {
    user = User.findById(userId);
} catch (NoSuchCategoryException e) {
    log.error('User {} doesn't exist.', userId);
    user = new User();
}

looks like a typical case where you shouldn't handle the error like that. "Fake" database objects can easily lead to code that assumes there was a proper entry. Assuming that's the case and you want to avoid exceptions you could do roughly

try {
    return Optional.of(User.findById(userId));
} catch (NoSuchCategoryException e) {
    log.error('User {} doesn't exist.', userId);
    return Optional.empty();
}

and handle the case with a clean abstraction. Or you return in case of exception but continue in case everything is ok. But continuing with a fake object is often not right. It can be.

like image 183
zapl Avatar answered Sep 27 '22 21:09

zapl