In my experience, when a function has more than 1 if statement, it's probably hard to test and you might want to split it up.
When a function has has more than 2 if statements, you definitely want to break it up.
Imagine the function where conditions aren't related at all, as you posited:
myfn(int x, int y, string foo) {
if(foo == "bar") {
do_stuff();
}
if( x
What's really happening here? Why is all this wrapped in 1 function, when the args aren't related at all, nor the work they are dependent on?
Let's say the caller was doing:
myfn(1,2,foo);
I would split this function into 3 different function calls in the caller...
possiblyDoStuff(foo);
possiblyDoOtherStuff(1,2);
possiblyDoOtherOtherStuff(time());
Let's use something that you wouldn't simply refactor upstream:
myfn(string go, string for, string foo) {
if(go == foo) {
do_stuff();
}
if(for == go) {
do_other_stuff();
}
if(foo == for) {
do_other_other_stuff();
}
someOtherFn(go, for, foo);
}
Move that complexity into smaller chunks:
myfn(string go, string for, string foo) {
possiblyDoStuff(go, foo);
possiblyDoOtherStuff(go, for);
possiblyDoOtherOtherStuff(for, foo);
someOtherFn(go, for, foo);
}
Now you have the 7 tests. 2 for each possibly = 6 which are pretty easy and 1 for myfn. It is exceedingly rare that functions require this kind of attention. There are usually a bunch of side effects or return values that are dependent on these checks. eg:
myfn(string go, string for, string foo) {
// examples of why you are calling these, to get vals
var go2 = possiblyDoStuff(go, foo);
var for2 = possiblyDoOtherStuff(go, for);
var foo2 = possiblyDoOtherOtherStuff(for, foo);
return { go2, for2, foo2 };
//move this to caller someOtherFn(myfn(go, for, foo));
}