Like the callback hell, our codes is also suffering from massive if-else statement. You must have ever saw some source codes like below:
1 |
|
Usually the if-else statement is added intuitively to support an additional case, or everyone just simply contribute one if
during maintenance which finally caused the hell: buggy, complex logic description, low readability, difficult to refactor.
Here is sharing some patterns which helps to eliminate massive if-else statements in our daily coding.
1. Guard Clauses
Sometimes the if-else
is added for code defense like below:
1 | fn (foo, bar) { |
In this case we can apply Guard Clause , leave the the if-else
away from business logic:
1 | fn (foo, bar) { |
2. Value Extraction
Below code is sample of bad smell, in real world it could be alive and hidden in more complex business logic codes:
1 | fn (arg) { |
Obviously in this case, the codes/value can be extracted as below:
1 | fn (arg) { |
or the ternary expression can be applied:
1 | fn (arg) { |
if the x
y
stands for boolean
, the value could be directly returned:
1 | fn (arg) { |
3. Apply Map or Array
Maybe below case can be improved by switch-case
, but also can try an array.
Before:
1 | fn (foo) { |
After:
1 | fn (foo) { |
Or some cases can be constructed to a map:
Before:
1 | fn (foo) { |
After:
1 | fn (foo) { |
One more tips, for some long if
condition could also be optimized by array or map.
Before:
1 | if (foo.bar == "aaa" || foo.bar == "bbb" || foo.bar == "ccc") { |
After:
1 | if (["aaa", "bbb", "ccc"].includes(foo.bar)) { |
Some idea from functional programming can be applied.
4. Apply Metadata / OOP
Below example can be applied in a more complex scenarios or high level design.
Say we have a render function to return data string by required format, original codes could be like below:
1 | fn render (data, format) { |
Maybe for this case we can apply a map as above in #3, but what if the logic here is much complexer and we want to modularize our source codes to improve scalability, so that we can consider OOP to abstract a renderer
interface for render class. Multiple different implementation for different format methods, like json renderer, xml renderer, html renderer. And we can tag the implementation classes, allow it self-describe its metadata. The technology here used is called Attribute in C#, Annotation in Java, Decorator in Python/ES2016/TS.
1 |
|
It’s exactly satisfied the OCP, once there is requirement for new formatter, just add a new class and zero-touched to existing logic.