3

I'm currently working on a big file I have to parse and process and each step needs to be done in an order as I do SQL queries and need inserted IDs to make other insertions ... The problem is I often find my code to look like this :

if( function1() ) {
    // process things to continue
    if( function2() ) {
        // process things to continue

        // ... and so on

    } else {
        // fail2
    }
} else {
    // fail1
}

And same goes for form validations, etc... and it gets really hard to follow the steps, and it's just not pleasant to see in the code.

As I said, I need function1 to be done before function2, so I can't do otherwise than be sure previous functions are done correctly.

Is there as proper way, or a design pattern, to code this kind of chained conditional functions ?

(FYI : I use OO PHP5, and I'm pretty much a novice in terms of standards in programming, so please don't behead me if this is casual !)

(concerning possible duplicate of When, if ever, should I daisy chain functions? ) : the functions can be used seperately and thus can't be used with daisy-chaining (see "B" in the given link).

The //Fails are here to "raise a flag" to say there has been a problem and further in the code (after the if/elses) it rollbacks all changes (with PDO's transaction rollback). But I would like to warn user there has been a problem, write a message (database error ? empty values ? ...) and maybe pass more informations like variables to be displayed/debugged.

10
  • Monads are nice for this; especially the Optional/Maybe monads. If your language supports them, I'd look into these. Jun 11, 2015 at 14:32
  • I use PHP5 and according to google, it doesn't natively support Monads (I didn't really grasp the concept of a Monad though). I'll check it anyway, thanks.
    – Strannch
    Jun 11, 2015 at 14:36
  • 3
    I'd say that it highly depends on the //fail part. For example if it's common to all elses, then maybe just throwing exception from functionN() would be fine?
    – Gerino
    Jun 11, 2015 at 14:37
  • 1
    see also: Elegant ways to handle if(if else) else
    – gnat
    Jun 11, 2015 at 21:21
  • 1
    Thank you @gnat. I should consider more orthogonality and probably apply the return/break solutions.
    – Strannch
    Jun 12, 2015 at 7:31

2 Answers 2

4

the easiest way to refactor that is to use a success variable, eg

res = do_stuff();
if (res)
    res = do_more_stuff();
if (res)
    ....

If you use a counter instead of a boolean success variable, then you can also tell if all of the steps succeeded at the end (ie. the counter will equal the number of steps that successfully ran)

The one thing you cannot do with this pattern is to have different 'else' clauses for error, but you can instead throw an exception to handle the flow (not so recommended if you expect the steps to fail). You could track the last method that was executed and have a switch handler at the end of the method that called the appropriate fail routine for each, but that can get messy - depends on your circumstances of course.

There are more complex ways to handle this - eg instead of calling each method, call a helper that takes 2 functions as parameters, the method to call and the method to call on failure, then the helper can return true/false as before, or construct a collection of functions to call and iterate through it, calling each one in turn.. but these are best left for special circumstances when you need them. As you say you're a bit of a novice, I'd go for the pattern I first suggested, its simple.

2
  • I think I will go with your first suggestion. Thanksyou. I let the question unanswered for now, but I'll validate it later if no one comes with other ideas ^^
    – Strannch
    Jun 11, 2015 at 15:17
  • I use this pattern too. Clear and simple, easy enough to follow and understand.
    – Spidey
    Jun 11, 2015 at 16:52
0

You can factor out the error handling, so that your function can simply return on error. In C/C++ this would look something like this:

int handleError(int errorCode, char* message) {
    if(errorCode) {
        //Notify user, whatever
    }
    return errorCode;
}

int functionThatCanFail(...) {
    int result;

    if(result = handleError(do_stuff(), "do_stuff() failed")) return result;
    if(result = handleError(do_more_stuff(), "do_more_stuff() failed")) return result;
}

Of course, you can have a small set of such error handling functions to distinguish, for example, between fatal errors, errors and warnings or that handle different error returning conventions (error code vs. NULL pointer, for instance). In C++, the later differentiation can be handled by a template, but that's not a language agnostic consideration anymore.

0

Not the answer you're looking for? Browse other questions tagged or ask your own question.