When the complexity of the logic becomes unwieldy then using an unbounded loop with mid-loop breaks for flow control does actually make the most sense. I have a project with some code which looks like the following. (Notice the exception at the end of the loop.)
L: for (;;) {
if (someBool) {
someOtherBool = doSomeWork();
if (someOtherBool) {
doFinalWork();
break L;
}
someOtherBool2 = doSomeWork2();
if (someOtherBool2) {
doFinalWork2();
break L;
}
someOtherBool3 = doSomeWork3();
if (someOtherBool3) {
doFinalWork3();
break L;
}
}
bitOfData = getTheData();
throw new SomeException("Unable to do something for some reason.", bitOfData);
}
progressOntoOtherThings();
To do this logic without unbounded loop breaking, I would end up with something like the following:
void method() {
if (!someBool) {
throwingMethod();
}
someOtherBool = doSomeWork();
if (someOtherBool) {
doFinalWork();
} else {
someOtherBool2 = doSomeWork2();
if (someOtherBool2) {
doFinalWork2();
} else {
someOtherBool3 = doSomeWork3();
if (someOtherBool3) {
doFinalWork3();
} else {
throwingMethod();
}
}
}
progressOntoOtherThings();
}
void throwingMethod() throws SomeException {
bitOfData = getTheData();
throw new SomeException("Unable to do something for some reason.", bitOfData);
}
So the exception is now thrown in a helper method. Also it has quite a lot of if ... else nesting. I'm not satisfied with this approach. Therefore I think the first pattern is best.