Extract it to separate function (method) and use return
statement:
if(FileExists(file))
{
contents = OpenFile(file); // <-- prevents inclusion in if
if(SomeTest(contents))
{
DoSomething(contents);
return;
}
}
DefaultAction();
Or, maybe better, separate getting contents and its processing:
contents_t get_contents(name_t file)
{
if(!FileExists(file))
return null;
contents = OpenFile(file);
if(!SomeTest(contents)) // like IsContentsValid
return null;
return contents;
}
...
contents = get_contents(file)
contents ? DoSomething(contents) : DefaultAction();
Upd:
Why not exceptions, why OpenFile
doesn't throw IO exception:
I think that it's really generic question, rather than question about file IO. Names like FileExists
, OpenFile
can be confusing, but if to replace them with Foo
, Bar
, etc, - it would be clearer that DefaultAction
may be called as often as DoSomething
, so it may be non-exceptional case. Péter Török wrote about this at end of his answer
Why there is ternary conditional operator in 2nd variant:
If there would be [C++] tag, I'd wrote if
statement with declaration of contents
in its condition part:
if(contents_t contents = get_contents(file))
DoSomething(contents);
else
DefaultAction();
But for other (C-like) languages, if(contents) ...; else ...;
is exactly the same as expression statement with ternary conditional operator, but longer. Because the main part of the code was get_contents
function, I just used the shorter version (and also omitted contents
type). Anyway, it's beyond this question.
DefaultAction
calls violate DRY principlemake sure I have valid data for DoSomething(), and then DoSomething() with it. Otherwise, take DefaultAction()
. The nitty gritty details of making sure you have the data for DoSomething() are at a lower abstraction level, and therefore should be in a different function. This function will have a name in the higher abstraction level, and its implementation will be low-level. The good answers below address this issue.