A good code pattern for single retry then error

algorithmsdesign-patterns

I am writing a routine which has the following form:

TRY A
IF no success, B
IF no success, RETRY A
IF no success, throw error

It's not trivial to extract either A or B into it's own routine, so what is most simple structure that will allow me to retry A without code duplication?

Currently, I have a do..while that allows N retries:

int retries = 1;

do {
    // DO A

    if ( /*success*/ ) {
        break;
    } else if (retries > 0)  {
        // DO B

        if ( /*success*/) {
            break;
        }
    } else {
        // throw Error
    }
} while (retries-- > 0);

This is ugly and not ideal as it implies that I might want to ever retry more than once, which I don't. I will have to use a loop, but there has to be a more simple way that I'm not seeing.

For context, this is code generated in Java, executing SQL statements to try an UPDATE first, then if no entry to update is found, INSERT, and if that command fails (concurrency, already created), try UPDATE again.

Best Answer

I've written this in C, so I can't guarantee that it'll cut-and-paste into Java.

If you're certain you want to retry DoA only once, use can use one line:

bool DoIt()
{
    return DoA() || DoB() || DoA();
}

bool DoA() { } // Return true if successful.

bool DoB() { } // Return true if successful.

If you want the option of more than one retry, use this loop.

bool DoIt(int retryLimit)
{
    if (DoA()) return true;
    for (int retries = 0; retries < retryLimit; retries++)
    {
        if (DoB()) return true;
        if (DoA()) return true;
    }
    return false;
}

Edit: If you really cannot make time to extract DoA and DoB, then try the following block. Remember that comments are your friend:

bool retry = false;
bool success = false;
do
{
    // Do A.
    success = // true or false
    if (success) break;

    // Attempt the above code twice and the below code once.
    if (retrying) break;
    retrying = true;

    // Do B.
    success = // true or false
    if (success) break;
}
if (success)
{
    // Celebrate.
}
else
{
    // Commiserate.
}
Related Topic