22

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.

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Martin Linha
  • 979
  • 9
  • 21
  • 38
    Why would you return an exception instead of throwing it? – Turing85 Apr 12 '18 at 12:29
  • 5
    @Turing85 This is what the whole question is about. :) – lexicore Apr 12 '18 at 12:30
  • @Turing85 ? You mean in `serviceUp()`? In `proceedWhenError()` we do not want to throw the exception. – Martin Linha Apr 12 '18 at 12:35
  • @lexicore: No, the question does not appear to mention throwing an exception as an option. – Mooing Duck Apr 12 '18 at 18:08
  • @MartinLinha: Yes. Why in the world is `serviceUp()` returning an exception, rather than throwing? – Mooing Duck Apr 12 '18 at 18:09
  • @MooingDuck Do you really think the only way to indicate some operation went wrong is by throwing an exception? See the accepted answer. – Martin Linha Apr 12 '18 at 18:26
  • 2
    I think the point of asking why it's returning the exception rather than allowing it to be thrown is that catching it, returning it, and checking for null is the same thing as just allowing it to be thrown (without catching it) and catching it on the level above, when calling the function, except it takes an extra step. In other words, any call to serviceUp() could be replaced by a call to service.connect() and the "if" replaced with a catch. – Corrodias Apr 12 '18 at 19:13
  • @MartinLinha: No, the vast majority of the time, I think returning a boolean is the right way to indicate if some operation went wrong. In rare cases, sometimes returning a status code, which provides more information. However, for _exceptional_ cases, which require even more data still, the proper thing to do is create an exception object which holds the required information, and then throw it. Which is what the accepted answer promotes in the second paragraph. – Mooing Duck Apr 13 '18 at 00:27
  • You had a [Smalltalk](https://en.wikipedia.org/wiki/Smalltalk) or a small talk? – CJ Dennis Apr 13 '18 at 02:18

12 Answers12

32

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.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
17

Second opinion ("it's alright") does not hold. The code is not alright because returning exceptions instead of throwing them is not really idiomatic.

I also don't buy the first opinion ("using Exceptions to control the workflow is anti-pattern"). service.connect() is throwing an exception and you have to react to this exception - so this is effectively flow control. Returning a boolean or some other state object and processing this instead of handling an exception - and thinking it's not control flow based on an exception is naive. Another disadvantage is that if you decide to rethrow an exception (wrapped in IllegalArgumentException or whatever), you won't have the original exception anymore. And this is extremely bad when you try to analyze what actually happened.

So I'd do the classic:

  • Throw the exception in serviceUp.
  • In methods invoking serviceUp:
    • try-catch, log debug and swallow the exception if you want to proceed on exception.
    • try-catch and rethrow the exception wrapped in another exception providing more information on what happened. Alternatively just let the original exception propagate via throws if you can't add anything substantial.

It is most important not to lose the original exception.

psmears
  • 26,070
  • 4
  • 40
  • 48
lexicore
  • 42,748
  • 17
  • 132
  • 221
  • 2
    I like this better than the accepted answer. It's simple. let proceedWhenError catch the exception and log it. serviceUp doesn't even need a try/catch. – dwilliss Apr 12 '18 at 21:23
  • 1
    @dwilliss And neither does doNotProceedWhenError, unless you _really_ need to wrap the exception in another exception. – Zeus Apr 12 '18 at 21:33
  • @Zeus True. Although I like to do that so that the main error logged says something like "Unable to connect to service XYZ" with an innerException of the actual error. Because the actual error will often be something vague like "connection refused", which doesn't tell you *what* connection was refused. Anything to help the support team so they don't have to call to ask what it means. – dwilliss Apr 13 '18 at 14:57
  • @dwilliss As a general rule, I tend to agree with you. – Zeus Apr 13 '18 at 20:33
7

Both are wrong

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

It is not alright. The concept of exceptions means that they are thrown in, well, exceptional cases. They are meant to be caught at the place they will be handled (or at the least re-thrown after some local cleanup/logging/etc.). They are not meant to be handed around like this (that is, in "domain" code).

People will be confused. Real bugs can easily creep it - for example, what if there is some Exception from some other source than networking here; one you did not anticipate, that is really bad (like some out-of-bounds exception that you created by some programming error)?

And fresh programmers will be endlessly confused, and/or copy that anti-pattern to places where it just doesn't belong.

For example, a colleague recently implemented a quite complicated interface (as in machine-to-machine interface, not Java interface), and he did a similar exception handling; converting exceptions to silently ignored variations (modulo some log messages). Needless to say, any exception that he was not actually expecting broke the whole mess in the worst imaginable way; the opposite of fail fast.

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.

Exceptions most certainly control the workflow in the aspect that they often abort it, or redirect to an error message displayed to the user. It is absolutely possible to have some part of the code "disabled" due to an exception; i.e., exception handling is surely allowed somewhere else than just at the top level of control.

But returning exceptions is indeed an anti-pattern; nobody expects that, it is weird, it leads to spurious errors (it is easy to ignore the return value) etc. etc.

So, in the case of your serviceUp(), either make it void - it either works 99% of the time, or throws an exception; or make it true boolean in that you fully well accept that it will fail somewhere. If you do need to hand the error message around, do it as a String, or save it somewhere out of the way, or something, but do not use it as return value, especially not if you intend to throw it again later.

Easy, standard solution

This solution is shorter (less lines, less variables, less if), simpler, bog-standard and does exactly what you wanted. Easy to maintain, easy to understand.

public void proceedWhenError() {
   try {
      serviceUp();
      // do stuff (only when no exception)
   }
   catch (Exception exception) {
      logger.debug("Exception happened, but it's alright.", exception)
      // do stuff (only when exception)
   }
}

public void doNotProceedWhenError() {
   try {
      serviceUp();
      // do stuff (only when no exception)
   }
   catch (Exception exception) {
      // do stuff (only when exception)
      throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", exception)
   }
}

private void serviceUp() {
    service.connect();
}
AnoE
  • 8,048
  • 1
  • 21
  • 36
  • Your proposed solution is the one that's the opposite of "fail fast", discarding "any exception that [you are] not actually expecting" thus hiding those problems and preventing them from being fixed. – Ben Voigt Apr 13 '18 at 02:02
  • @BenVoigt, that example is a simple, direct rewrite of the original code, showing how exactly the same as he did can be achieved without handing an Exception object around, or doing `if` clauses. – AnoE Apr 13 '18 at 05:59
3

I would return a ServiceState which can be, for instance, RUNNING, WAITING, CLOSED. The method would be named getServiceState.

enum ServiceState { RUNNING, WAITING, CLOSED; }

I have never seen the methods that return an exception as a result of the execution. For me, when a method returns the value, it means the method finished its work without issues. I don't want to retrieve the result and parse it on containing any error. The result itself means that no failures happened - everything went as planned.

On the other hand, when the method throws an exception, I need to parse a special object to figure out what went wrong. I don't parse the result, because there is no result.

An example:

public void proceedWhenError() {
   final ServiceState state = getServiceState();

   if (state != ServiceState.RUNNING) {
      logger.debug("The service is not running, but it's alright.");
   }
   // do stuff
}

public void doNotProceedWhenError() {
   final ServiceState state = getServiceState();

   if (state != ServiceState.RUNNING) {
      throw new IllegalStateException("The service is not running...");
   }
   // do stuff
}

private ServiceState getServiceState() {
    try {
        service.connect();
        return ServiceState.RUNNING;
    catch(Exception e) {
        // determine the state by parsing the exception
        // and return it
        return getStateFromException(e);
    }
}

If the exceptions thrown by the service is important and/or processed in another place, it along with the state could be saved into a ServiceResponse object:

class ServiceResponse {

    private final ServiceState state;
    private final Exception exception;

    public ServiceResponse(ServiceState state, Exception exception) {
        this.state = state;
        this.exception = exception;
    }

    public static ServiceResponse of(ServiceState state) {
        return new ServiceResponse(state, null);
    }

    public static ServiceResponse of(Exception exception) {
        return new ServiceResponse(null, exception);
    }

    public ServiceState getState() {
        return state;
    }

    public Exception getException() {
        return exception;
    }

}

Now, with ServiceResponse, these methods might look like:

public void proceedWhenError() {
   final ServiceResponse response = getServiceResponse();

   final ServiceState state = response.getState();
   final Exception exception = response.getException();

   if (state != ServiceState.RUNNING) {
      logger.debug("The service is not running, but it's alright.", exception);
   }
   // do stuff
}

public void doNotProceedWhenError() {
   final ServiceResponse response = getServiceResponse();

   final ServiceState state = response.getState();
   final Exception exception = response.getException();

   if (state != ServiceState.RUNNING) {
      throw new IllegalStateException("The service is not running...", exception);
   }
   // do stuff
}

private ServiceResponse getServiceResponse() {
    try {
        service.connect();
        return ServiceResponse.of(ServiceState.RUNNING);
    catch(Exception e) {
        // or -> return ServiceResponse.of(e);
        return new ServiceResponse(getStateFromException(e), e);
    }
}
Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
1

returning an Exception is indeed an anti pattern becuase Exceptions should be reserved for errors in the execution, not to describe the condition of the service.

imagine if there is a bug in the code of serviceUp() that causes it to throw NullPointerException. Now imagine the bug is in the service and the same NullPointerException is thrown from the connect().

See my point?

Another reason is changing requirements.

Currently, the service has two conditions: either up or down.
Currently.

Tommorow, you will have three conditions for the service: up, down. or functioning with warnings. The day after, you will also want the method to return details about the service in json.....

Sharon Ben Asher
  • 13,849
  • 5
  • 33
  • 47
1

The obvious cognitive dissonance is the anti-pattern here. A reader will see you using exceptions for flow control and a developer would immediately try to recode it so it does not.

My instinct suggests an approach like:

// Use an action name instead of a question here because it IS an action.
private void bringServiceUp() throws Exception {

}

// Encapsulate both a success and a failure into the result.
class Result {
    final boolean success;
    final Exception failure;

    private Result(boolean success, Exception failure) {
        this.success = success;
        this.failure = failure;
    }

    Result(boolean success) {
        this(success, null);
    }

    Result(Exception exception) {
        this(false, exception);
    }

    public boolean wasSuccessful() {
        return success;
    }

    public Exception getFailure() {
        return failure;
    }
}

// No more cognitive dissonance.
private Result tryToBringServiceUp() {
    try {
        bringServiceUp();
    } catch (Exception e) {
        return new Result(e);
    }
    return new Result(true);
}

// Now these two are fine.
public void proceedWhenError() {
    Result result = tryToBringServiceUp();
    if (result.wasSuccessful()) {
        // do stuff
    } else {
        logger.debug("Exception happened, but it's alright.", result.getFailure());
        // do stuff
    }
}

public void doNotProceedWhenError() throws IllegalStateException {
    Result result = tryToBringServiceUp();
    if (result.wasSuccessful()) {
        // do stuff
    } else {
        // do stuff
        throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", result.getFailure());
    }
}
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
1

If a method's callers will be expecting that an operation might succeed or fail, and are prepared to handle either case, then the method should indicate, typically via return value, whether the operation failed. If the callers aren't prepared to usefully handle failure, then the method in which the failure occurred should throw the exception rather than requiring callers add code to do so.

The one wrinkle is that some methods will have some callers that are prepared to gracefully handle failures and others that aren't. My preferred approach would be to have such methods accept an optional callback which is invoked in case of failure; if no callback is supplied, the default behavior should be to throw an exception. Such an approach would save the cost of constructing an exception in cases where a caller is prepared to handle a failure, while minimizing the burden upon callers that aren't. The biggest difficulty with such a design is deciding what parameters such a callback should take, since changing such parameters later is apt to be difficult.

supercat
  • 77,689
  • 9
  • 166
  • 211
0

You can't tell the 2) opinion is false, cause the workflow will run as you want it to run. From a logical point of view it will run as wanted so it's correct.

But it's a really strange and not recommended way to do it. Firstly because Exception are not designed to do that (which means you are doing an anti-pattern). It's a specific object designed to be thrown and catch. So it's strange to, rather used it as designed, choose tor return it and rather catch it use a if on it. Moreover you will (maybe it's negligible) face a performance issue cause rather than using a simple boolean as a flag, you instantiate a whole object.

Finally it's also not recommended cause function should return something if the goal of the function is to obtain something (which is not your case). You should re-designed it to be a function that start a service, then it return nothing cause it won't be called to obtain something, and it will throw an exception if an error happened. And if you want to know if your service work create a function designed to give you this info like public boolean isServiceStarted().

vincrichaud
  • 2,218
  • 17
  • 34
0

There are three (idiomatic) to handle functions that can fail during runtime.

1. Return boolean

The first one is to return boolean. This style is widely used in C and C-style APIs, like PHP. This leads - for example in PHP - often to code like this:

if (xyz_parse($data) === FALSE)
    $error = xyz_last_error();

The downsides to this are obvious, this is why it has fallen out of fashion more and more, especially in OOP languages and languages with exception-handling.

2. Use an intermediary/state/result object

If you do not use exceptions, you still have the opportunity in OOP languages to return objects that describe the state.

For example, if you receive input from the user and want to validate the input, you know beforehand that you might get garbage and that the result will quite often not validate, and you might write code like this:

ValidationResult result = parser.validate(data);
if (result.isValid())
    // business logic
else
    error = result.getvalidationError();

3. Use exceptions

Java does this as you have shown with sockets. The reasoning is that creating a connection should succeed and only fails in exceptional circumstances which need special handling.

Applying it

In your case, I would just throw the exception directly, and even drop the helper method.Your helper method is badly named. It doesn't actually query whether the service is up (as the name suggests), but instead simply connects.

Use (checked) exceptions

Lets assume that connecting is what your method actually does. In this case, we name it more aptly connectToService and make it throw the exception directly:

public void connectToService() thows IOException {
    // yada yada some init stuff, whatever
    socket.connect();
}

public void proceedWhenError() {
   try {
        connectToService();
   } else {
      logger.debug("Exception happened, but it's alright.", exception)
      // do stuff
   }
}

public void doNotProceedWhenError() throws IllegalStateException {
    try  {
        connectToService();
        // do stuff
    }
    catch(IOException e) {
      throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", exception)
   }
}

Use boolean

On the other hand, it might be possible that your serviceUp (which is still badly named and would better be called isServiceUp) does actually query whether the service is running or not (which might have been started by someone else). In this case, using a boolean would be the proper way

public boolean isServiceUp {
    return ...; // query the service however you need
}

This, of course, refers to the first solution of using booleans and isn't really helpful when you also need to know why the service isn't up.

Use intermediary/result/state objects

So lets dismiss this limited approach and rework it using an intermediary/result/state object:

class ServiceState {
    enum State { CONNECTED, WAITING, CLOSED; }

    State state;
    IOException exception;
    String additionalMessage;

    public ServiceState (State state, IOException ex, String msg) {
         [...] // ommitted for brevity
    }

   // getters ommitted for brevity
}

The helper function would become getServiceState:

public ServiceState getServiceState() {
    // query the service state however you need & return
}

public void proceedWhenError() {
   ServiceState state = getServiceState();

   if (state.getState() == State.CONNECTED) {
      // do stuff
   } else {
      logger.debug("Exception happened, but it's alright.", state.getException())
      // do stuff
   }
 }

public void doNotProceedWhenError() {
    ServiceState state = getServiceState();

   if (state.getState() == State.CONNECTED) {
      // do stuff
   } else {
      // do stuff
      throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", exception)
   }
}

Note that you are also repeating yourself in proceedWhenError, the whole code could be simplified to:

public void proceedWhenError() {
   ServiceState state = getServiceState();

  if (state.getState() != State.CONNECTED) {
      logger.debug("Exception happened, but it's alright.", state.getException())

   }
 // do stuff
}

There is a bit of debate when to use the second case, and when to use the third. Some people believe that exceptions should be exceptional and that you should not design with the possibility of exceptions in mind, and will pretty much always use the second option. That is fine. But we have checked exceptions in Java, so I see no reason not to use them. I use checked exceptions when the basic assumption is that the call should succeed (like using a socket), but failure is possible, and I use the second option when its very unclear whether the call should succeed (like validating data). But there are different opinions on this.

It also depends on what your helper method actually does. isServiceUp implies that it queries some state, but doesn't change it. In that case, returning an state object is obviously the idiomatic way to handle this.

But your implementation shows that the helper method connects to the service. In which case, at least in java, the idiomatic way to handle it would be to throw a (checked) exception - but you could also still justify using a result object.

Simply returning boolean is not advisable, though, since it is too limited. If all you need to know whether the service is running or not (and aren't interested in the cause), such a method might still be useful, though (and could, behind the scenes, be implemented as helper method that just does return getServiceState() == State.SUCCESS).


It is an anti-pattern to use Exceptions to control the workflow

So, lets see, what is an exception?

Definition: An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program's instructions.

Source: https://docs.oracle.com/javase/tutorial/essential/exceptions/definition.html

The very definition of an exception is that it is an event that disprupts (and thus changes) the workflow of the program. If using it as defined is an anti-pattern, then the whole language feature in itself must be considered an anti-pattern. There are people that do believe exceptions are bad, and some have some valid arguments for it, but in general, exceptions are regarded as useful constructs and seen as useful tools to handle exceptional workflows.

Throwing an exception is certainly not an anti-pattern. Returning it, on the other hand, is. Thus yes, your code - as presented - is non-idiomatic.

Polygnome
  • 7,639
  • 2
  • 37
  • 57
0

This is an anti-pattern:

catch(Exception e) {
   return e;
}

The only reasonable excuse for returning Throwable is to provide detailed failure information to a fast path where failure is expected, without paying the price1 of exception handling. As a bonus, it makes it easy to convert to a thrown exception if the caller doesn't know how to handle this particular situation.

But in your scenario, that cost has already been paid. Catching the exception on behalf of your caller does no one any favors.


1Pedantically, the direct cost of catching an exception (finding a matching handler, stack unwinding) is pretty low or needed to be done anyway. Most of the cost of try/catch compared to a return value is in incidental actions, like building a stack trace. So whether unthrown exception objects make good storage for efficient error data depends on whether this incidental work is done at time of construction or time of throwing. Therefore whether returning an exception object is reasonable may differ between different managed platforms.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
0

As mentioned in earlier comments "Exception is an event" the exception object that we get is just a detail of the event. Once an Exception is caught in "catch" block and not re-thrown , the event is over. Post that you just have a detail object not an exception, although the object is of class exception/throwable.

Returning the exception object might be useful because of details held by it but it will add ambiguity/confusion as the calling method is not "handling exception and controlling flow based on exception" . It is merely using details of a returned object to make decisions.

So in my opinion a more logical and less confusing way will be to return a boolean/enum based on the exception rather than simply returning exception object.

dev. K
  • 63
  • 1
  • 6
0

Not per-se. For example, if you're working with something like AggregateExceptions from .NET, you have to collect some other exceptions first before you can throw anything. Stuff like that could involve a method that creates (and returns) your exception.

The more important question in my opinion is, whether or not you intend to throw this exception eventually.

If not, I think it's bad practice, because this is the one thing the Exception base class does. Other than that it just holds some primitive properties. It would be more semantic (and less confusing for the reader) to create a class with a better name that represents those values.

Oliver Schimmer
  • 387
  • 2
  • 14