Even then, the transformation doesn't reduce complexity. It just moves complexity around the program in a way that may make practical risks worse.
In the first case, there are lots of branches in the top-level function, but the preconditions of each function are stronger. This means f10() doesn't have to recheck f1()'s work, and neither do f9(), f8(), etc. A bug in f3() that fails to detect a violation of f4()'s preconditions could ruin our day, but f4() is simpler in this case, and if we get f3() wrong we'll probably get f4() wrong at the same time anyway.
In the second case, there are fewer branches at the top level, but the invariants, preconditions, and postconditions of the functions are weaker. f10() has to cope with everything f1() let through, and so does f9(), f8(), etc. This can breed code duplication and maintenance mistakes, and increases the attack surface considerably. Imagine fixing six slightly different string-quoting bugs because every function after f3() has to check for conditions that f3() already identified as part of an error state. We can avoid this failure mode by having every function check *errors and return early, but then we've just created a variation on the first case that the compiler can't optimize as easily, and that requires wrapper functions around libraries that don't use this convention.
Either one has a linearly expanding set of states inside example(), assuming you don't make a dumb semantic mistake. There's pretty much no difference from a formal coverage testing point of view.
Copyright © 2017, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds