简体   繁体   中英

What is an alternative to exceptions for flow control?

I inherited a java application that processes requests and throws an exception if it determines a request should be cancelled. Exceptions have been convenient for the previous developer because they are an easy way to exit out of a tree of logic that no longer applies (yes a goto) and it prints a stack trace to the log which is good info to have. It seems to be the consenus on forums and blogs that exceptions should not be used for flow control and over half of the requests processed are cancelled, so they're definitely not exceptional situations. One argument is performance, which doesn't apply because our exception-ful code has run fast enough for years. Another argument is that the goto-ness of it is not clear, which I agree with. My question is: What is the alternative. The only thing I can think of is to have every method return true if processing should continue or false if it shouldn't. This seems like it would hugely bloat my code changing this:

checkSomething();
checkSomethingElse();

into this:

boolean continueProcessing = false;
continueProcessing = checkSomething();
if (!continueProcessing) {
    return false;
}
continueProcessing = checkSomethingElse();
if (!continueProcessing) {
    return false;
}

And then what if the method is supposed to return something? Any guidance would be great. I'd really like to observe whatever "best practices" are available.

Update:

Another bit I probably should have mentioned in the first place is that a request is cancelled more than 50% of the time and does not mean something didn't go right, it means the request wasn't needed after all.

In your scenario, the alternatives are throwing an exception and returning a result.

Which is best (and which is "best practice") depends on whether the failure of the "check..." methods should be classed as normal or exceptional. To some degree this is a judgement call that you have to make. In making that call there are a couple of things to bear in mind:

  • Creating + throwing + catching an exception is roughly 3 orders of magnitude SLOWER than testing a boolean result.

  • The big problem with returning status codes is that it is easy to forget to test them.

In summary, best practice is to not use exceptions to implement normal flow control, but it is up to you to decide where the borderline between normal and exceptional is.


Without seeing the real code / context, my gut feeling is that this is likely to be an appropriate place to use exceptions.

See How slow are Java exceptions? for a great discussion on this topic.

tl;dr

Separation of Concerns , and IMO you should do this:

continue = checkSomething() && checkSomethingElse();

or perhaps there are other design problems in the code.



What's the concern of the function -- as you want to define it (this can be a subjective question)? If the error fits into the function's concern, then don't throw an exception . If you are confused about whether or not the error fits into the concern, then perhaps the function is trying to accomplish too many concerns on its own (bad design).

Error control options:

  1. don't report error. It either is handled directly by function or doesn't matter enough
  2. return value is
    1. null instead of an object
    2. the error information (perhaps even a different data type than the object returned on success).
  3. an argument passed in will be used to store error data.
  4. trigger an event
  5. call a closure passed to function if an error occurs.
  6. throw an exception. (I'm arguing this should usually only be done if it's not a part of the arbitrarily defined purpose of the function)

If the purpose of the code is to check some state, then knowing that the state is false is directly the point of the function. Don't throw an exception, but return false.

That's what it looks like you are wanting. You have process X which is running checkers Y and Z. Control flow for process X (or any calling process) is not the same concern as checking states of a system.

int error = 0;

do
{//try-alt
    error = operation_1(); if(error > 0){break;}
    error = operation_2(); if(error > 0){break;}
    error = operation_3(); if(error > 0){break;}
}while(false);

switch(error) //catch-alt
{
 case 1: handle(1); break;
 case 2: handle(2); break;
 case 3: handle(3); break;   
}

How about

if (!checkSomething()) return false;
if (!checkSomethingElse()) return false;

No need for the flag if you exit early.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM