I have two simple methods:
public void proceedWhenError() { Throwable exception = serviceUp(); if (exception == null) { // do stuff } else { logger.debug("Exception happened, but it's alright.", exception) // do stuff } } public void doNotProceedWhenError() { Throwable exception = serviceUp(); if (exception == null) { // do stuff } else { // do stuff throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", exception) } }
The third method is a private helper method:
private Throwable serviceUp() { try { service.connect(); return null; catch(Exception e) { return e; } }
We had a small talk with a colleague of mine about the pattern used here:
returning an Exception
(or Throwable
) object from the serviceUp()
method.
The first opinion:
It is an anti-pattern to use Exceptions to control the workflow and we should only return boolean from
serviceUp()
and never the Exception object itself. The argument is that using Exceptions to control the workflow is an anti-pattern.
The second opinion:
It is alright, as we need to deal with the object afterwards in the two first methods differently and whether returning Exception object or boolean does not change the workflow at all
Do you think 1) or 2) is correct and especially, why? Note, that the question is ONLY about the method serviceUp()
and its return type - boolean
vs Exception
object.
Note: I am not questioning whether to use Throwable or Exception objects.
According to many references like here and here, using Exceptions to control application flow is an anti-pattern that is not recommended. (Because of performance issues, Less readable and etc).
Use exceptions to report and handle error conditions. Exceptions shouldn't be returned as a return value or parameter instead of being thrown. Don't throw System. Exception, System.
It's not possible to both throw an exception and return a value from a single function call.
Since it returns a boolean value, there would be no need of throwing an exception, simply return false.
It is an anti-pattern to use exceptions to direct the flow only when the exception is thrown in a non-exceptional situation*. For example, ending a loop by throwing an exception when you reach the end of a collection is an anti-pattern.
Controlling the flow with actual exceptions, on the other hand, is a good application of exceptions. If your method encounters an exceptional situation that it cannot handle, it should throw an exception, thus re-directing the flow in the caller to the exception handler block.
Returning a "naked" Exception
object from a method, rather than throwing it, is certainly counter-intuitive. If you need to communicate the results of an operation to the caller, a better approach is to use a status object that wraps all the relevant information, including the exception:
public class CallStatus { private final Exception serviceException; private final boolean isSuccess; public static final CallStatus SUCCESS = new CallStatus(null, true); private CallStatus(Exception e, boolean s) { serviceException = e; isSuccess = s; } public boolean isSuccess() { return isSuccess; } public Exception getServiceException() { return serviceException; } public static CallStatus error(Exception e) { return new CallStatus(e, false); } }
Now the caller would receive CallStatus
from serviceUp
:
CallStatus status = serviceUp(); if (status.isSuccess()) { ... // Do something } else { ... // Do something else Exception ex = status.getException(); }
Note that the constructor is private, so serviceUp
would either return CallStatus.SUCCESS
or call CallStatus.error(myException)
.
* What is exceptional and what is not exceptional depends a great deal on the context. For example, non-numeric data causes an exception in Scanner
's nextInt
, because it considers such data invalid. However, the same exact data does not cause an exception in hasNextInt
method, because it is perfectly valid.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With