IIRC “single exit” wasn’t supposed to mean “only exit from one place” but “only exit TO one place.” Things like exceptions break that, as the function can now exit both to the main path and to the exception handler.
Erm, okay, if that happens to work for you (for some definition of “works for you”), great. I’m happy for you. Power to you. Keep doing it. But here’s why I deliberately code with almost the polar opposite of this strategy:
Refactoring to use guard clauses and multiple returns does not actually change the tree of execution paths. A simple example (in Ruby): Consider these two method bodies.
if foo
process foo
else
raise 'error'
end
is equivalent to
raise 'error' if ! foo
process foo
But the second one doesn’t resemble or represent the execution path tree like the first one does. So, in your effort to reduce whitespace or indentation, you’ve actually hid or obscured that tree, instead of communicating the tree more obviously and clearly.
We do want to reduce conditional branching (ABC score, Cyclomatic Complexity, yadda yadda), but I think a far superior way to approach refactors like this is to outright remove conditions with strategies like the Null Object Pattern. Example:
class Foo < Model; end
foo = Foo.find(id)
if ! foo.nil?
foo.bar
end
can become
class Foo < Model; end
class NilFoo
def bar; end
end
foo = Foo.find(id) || NilFoo.new
foo.bar
where there is no conditional used any more.
Yes, I’m aware we can write foo&.bar in the first snippet, but the power of the Null Object Pattern is still applicable in non-trivial, real-world cases where you’d otherwise be littering your code with nil checks (thereby increasing the ABC score). And, yes, I understand that using || in lieu of the nil check in this example does not reduce the number of execution paths, but that’s because this example is so simple. That single || can take the place of many multiple nil checks in a real-world codebase.
Yes, I’m aware we can write foo&.bar in the first snippet, but the power of the Null Object Pattern is still applicable in non-trivial, real-world cases where you’d otherwise be littering your code with nil checks (thereby increasing the ABC score)
IIRC “single exit” wasn’t supposed to mean “only exit from one place” but “only exit TO one place.” Things like exceptions break that, as the function can now exit both to the main path and to the exception handler.
I’d heard this too. Seems it’s another piece of Fortran-inherited folk wisdom, like
gotobeing inherently evil.https://softwareengineering.stackexchange.com/a/118793
Erm, okay, if that happens to work for you (for some definition of “works for you”), great. I’m happy for you. Power to you. Keep doing it. But here’s why I deliberately code with almost the polar opposite of this strategy:
Refactoring to use guard clauses and multiple
returns does not actually change the tree of execution paths. A simple example (in Ruby): Consider these two method bodies.is equivalent to
But the second one doesn’t resemble or represent the execution path tree like the first one does. So, in your effort to reduce whitespace or indentation, you’ve actually hid or obscured that tree, instead of communicating the tree more obviously and clearly.
We do want to reduce conditional branching (ABC score, Cyclomatic Complexity, yadda yadda), but I think a far superior way to approach refactors like this is to outright remove conditions with strategies like the Null Object Pattern. Example:
can become
where there is no conditional used any more.
Yes, I’m aware we can write
foo&.barin the first snippet, but the power of the Null Object Pattern is still applicable in non-trivial, real-world cases where you’d otherwise be littering your code withnilchecks (thereby increasing the ABC score). And, yes, I understand that using||in lieu of thenilcheck in this example does not reduce the number of execution paths, but that’s because this example is so simple. That single||can take the place of many multiple nil checks in a real-world codebase.What’s wrong with increasing the ABC score?
The theory goes that more complicated code is harder to safely change and harder to debug. My personal experience bears this out.