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.

有帮助吗?

解决方案

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.

其他提示

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.

许可以下: CC-BY-SA归因
scroll top