It is time to break some bad habits, and today we take some code written by a man
I like (but who will remain nameless and linkless) that is in desperate need of refactoring.
But this problem is not a problem exclusive to this very bright developer, it is a
bad habit that we as an industry have picked up somewhere along the line and I’m here
to say “Stop The Insanity!”.
Consider the following code:
1: if (_privateDataObject.Methodology != null)
2: {
3: if (_privateDataObject.Methodology == "00")
4: {
5: if (_privateDataObject.Amount != null)
6: {
7: if (IsNumeric(_privateDataObject.Amount))
8: {
9: if (Convert.ToDecimal(_privateDataObject.Amount )==0 )
10: {
11: bReturnValue = true;
12: }
13: else
14: {
15:
16: bReturnValue = false;
17: }
18: }
19: else
20: {
21:
22: bReturnValue = false;
23: }
24: }
25: else
26: {
27:
28: bReturnValue = false;
29: }
30: }
31: else
32: {
33:
34: bReturnValue = false;
35: }
36: }
37:
38: return bReturnValue;
.csharpcode-wrapper, .csharpcode-wrapper pre {
background-color: #f4f4f4;
border: solid 1px gray;
cursor: text;
font-family: consolas, ‘Courier New’, courier, monospace;
font-size: 8pt;
line-height: 12pt;
margin: 20px 0px 10px 0px;
max-height: 200px;
overflow: auto;
padding: 4px 4px 4px 4px;
width: 97.5%;
}
.csharpcode-wrapper pre {
border-style: none;
margin: 0px 0px 0px 0px;
overflow: visible;
padding: 0px 0px 0px 0px;
}
.csharpcode, .csharpcode pre, .csharpcode .alt {
background-color: #f4f4f4;
border-style: none;
color: black;
font-family: consolas, ‘Courier New’, courier, monospace;
font-size: 8pt;
line-height: 12pt;
overflow: visible;
padding: 0px 0px 0px 0px;
width: 100%;
}
.csharpcode pre {
margin: 0em;
}
.csharpcode .alt {
background-color: white;
}
.csharpcode .asp {
background-color: #ffff00;
}
.csharpcode .attr {
color: #ff0000;
}
.csharpcode .html {
color: #800000;
}
.csharpcode .kwrd {
color: #0000ff;
}
.csharpcode .lnum {
color: #606060;
}
.csharpcode .op {
color: #0000c0;
}
.csharpcode .preproc {
color: #cc6633;
}
.csharpcode .rem {
color: #008000;
}
.csharpcode .str {
color: #006080;
}
This decision weighs in at over 38 lines. Let’s look at what the warning signs in
this method should have been that it needed to be coded differently.
The first and most important is that this follows a very basic pattern we see in code,
we are seeking for single complex condition and want to return a “true” if we find
it, and “false” in all other cases. This pattern is so basic it is taught in every
Intro to Programming course ever. The pattern has a common name “Boolean AND” and
is a part of every language (that I know of) on the planet. Refactored to accomidate
for this pattern we end up with :
1: if (_privateDataObject.Methodology != null &&
2: _privateDataObject.Methodology == "00" &&
3: _privateDataObject.Amount != null &&
4: IsNumeric(_privateDataObject.Amount) &&
5: Convert.ToDecimal(_privateDataObject.Amount ) ==
0)
6: {
7: bReturnValue = true;
8: }
9: else
10: {
11: bReturnValue = false;
12: }
13:
14: return bReturnValue;
.csharpcode-wrapper, .csharpcode-wrapper pre {
background-color: #f4f4f4;
border: solid 1px gray;
cursor: text;
font-family: consolas, ‘Courier New’, courier, monospace;
font-size: 8pt;
line-height: 12pt;
margin: 20px 0px 10px 0px;
max-height: 200px;
overflow: auto;
padding: 4px 4px 4px 4px;
width: 97.5%;
}
.csharpcode-wrapper pre {
border-style: none;
margin: 0px 0px 0px 0px;
overflow: visible;
padding: 0px 0px 0px 0px;
}
.csharpcode, .csharpcode pre, .csharpcode .alt {
background-color: #f4f4f4;
border-style: none;
color: black;
font-family: consolas, ‘Courier New’, courier, monospace;
font-size: 8pt;
line-height: 12pt;
overflow: visible;
padding: 0px 0px 0px 0px;
width: 100%;
}
.csharpcode pre {
margin: 0em;
}
.csharpcode .alt {
background-color: white;
}
.csharpcode .asp {
background-color: #ffff00;
}
.csharpcode .attr {
color: #ff0000;
}
.csharpcode .html {
color: #800000;
}
.csharpcode .kwrd {
color: #0000ff;
}
.csharpcode .lnum {
color: #606060;
}
.csharpcode .op {
color: #0000c0;
}
.csharpcode .preproc {
color: #cc6633;
}
.csharpcode .rem {
color: #008000;
}
.csharpcode .str {
color: #006080;
}
From 38 lines down to 14, not bad. And this version is not even as short (in line
count) as it could be in favor of readability (spreading the expression across multiple
lines) and to include braces (which are optional with only 1 command).
Now that we’ve tamed the wild nested if, let’s look at another problem. Why set a
value, only to immediately return that value once you’ve decided on the result? Just
return the value immediately. This small win nets us the following code:
1: if (_privateDataObject.Methodology != null &&
2: _privateDataObject.Methodology == "00" &&
3: _privateDataObject.Amount != null &&
4: IsNumeric(_privateDataObject.Amount) &&
5: Convert.ToDecimal(_privateDataObject.Amount ) ==
0)
6: {
7: return true;
8: }
9: else
10: {
11: return false;
12: }
.csharpcode-wrapper, .csharpcode-wrapper pre {
background-color: #f4f4f4;
border: solid 1px gray;
cursor: text;
font-family: consolas, ‘Courier New’, courier, monospace;
font-size: 8pt;
line-height: 12pt;
margin: 20px 0px 10px 0px;
max-height: 200px;
overflow: auto;
padding: 4px 4px 4px 4px;
width: 97.5%;
}
.csharpcode-wrapper pre {
border-style: none;
margin: 0px 0px 0px 0px;
overflow: visible;
padding: 0px 0px 0px 0px;
}
.csharpcode, .csharpcode pre, .csharpcode .alt {
background-color: #f4f4f4;
border-style: none;
color: black;
font-family: consolas, ‘Courier New’, courier, monospace;
font-size: 8pt;
line-height: 12pt;
overflow: visible;
padding: 0px 0px 0px 0px;
width: 100%;
}
.csharpcode pre {
margin: 0em;
}
.csharpcode .alt {
background-color: white;
}
.csharpcode .asp {
background-color: #ffff00;
}
.csharpcode .attr {
color: #ff0000;
}
.csharpcode .html {
color: #800000;
}
.csharpcode .kwrd {
color: #0000ff;
}
.csharpcode .lnum {
color: #606060;
}
.csharpcode .op {
color: #0000c0;
}
.csharpcode .preproc {
color: #cc6633;
}
.csharpcode .rem {
color: #008000;
}
.csharpcode .str {
color: #006080;
}
From 14 to 12, not bad, and even less un-needed code. The final refactoring is based
on the fact that this code is searching for a boolean value using boolean expressions.
That means that to reach true “ruthless” refactoring we should end up with the following:
1: return _privateDataObject.Methodology != null &&
2: _privateDataObject.Methodology == "00" &&
3: _privateDataObject.Amount != null &&
4: IsNumeric(_privateDataObject.Amount) &&
5: Convert.ToDecimal(_privateDataObject.Amount ) ==
0;
.csharpcode-wrapper, .csharpcode-wrapper pre {
background-color: #f4f4f4;
border: solid 1px gray;
cursor: text;
font-family: consolas, ‘Courier New’, courier, monospace;
font-size: 8pt;
line-height: 12pt;
margin: 20px 0px 10px 0px;
max-height: 200px;
overflow: auto;
padding: 4px 4px 4px 4px;
width: 97.5%;
}
.csharpcode-wrapper pre {
border-style: none;
margin: 0px 0px 0px 0px;
overflow: visible;
padding: 0px 0px 0px 0px;
}
.csharpcode, .csharpcode pre, .csharpcode .alt {
background-color: #f4f4f4;
border-style: none;
color: black;
font-family: consolas, ‘Courier New’, courier, monospace;
font-size: 8pt;
line-height: 12pt;
overflow: visible;
padding: 0px 0px 0px 0px;
width: 100%;
}
.csharpcode pre {
margin: 0em;
}
.csharpcode .alt {
background-color: white;
}
.csharpcode .asp {
background-color: #ffff00;
}
.csharpcode .attr {
color: #ff0000;
}
.csharpcode .html {
color: #800000;
}
.csharpcode .kwrd {
color: #0000ff;
}
.csharpcode .lnum {
color: #606060;
}
.csharpcode .op {
color: #0000c0;
}
.csharpcode .preproc {
color: #cc6633;
}
.csharpcode .rem {
color: #008000;
}
.csharpcode .str {
color: #006080;
}
Final result, 5 lines of code, same functionality. Important note to the Visual Basic
developers out there, && is the equivalent of “AndAlso” in VB, not simply
“And”, hence why this code does not blow up with a NullReferenceException.
I implore every developer reading this to get into the habit of considering how to
refactor code as soon as you’ve got it working, and I mean on a method by method basis.
Even if you’re not using a “TDD” methodology, this is a very good habit to get into
because every time we change code we’ve introduced the chance to improve either the
readability or performance of the code as well. It is natural to reach “working” code
and want to move on, but spending 10 minutes to examine how you got it working will
pay off in the long run, I assure you.