Leaving all former and historical discussion on "why not using switch
statements" behind, I'd like to show my approach on how to get the most out of it:
function processByType ({ type, ...args }) {
switch (type) {
case CONST_VALUE_A:
return processArgsA(args)
case CONST_VALUE_B:
return processArgsB(args)
case CONST_VALUE_C:
return processArgsC(args)
default:
throw new Error(`unknown type ${type}`)
}
}
Let me explain a bit, why I think this is a great switch
concept:
Single responsibility
I think we should approach the switch
with something similar to the single responsibility principle.
Basically, it's purpose is to decide which case branch to execute by a given value. The only reason to change the switch code is an updated logic on the value mapping (which case branch to execute, based on which value of type
).
Derived from this assumption, we may conclude the following:
It has no operational code in branches
Let the switch
defer processing to external functions, because it is not the responsibility of the switch
to implement the concrete processing.
It should also contain no pre- or post-processing code but just pass through arguments from input to the defined functions.
It is wrapped in a function
The switch
can only be isolated when wrapped in a function. Also increased the reuse and testability.
It uses constants as case
match conditions
This is a tricky one and I am 50/50 on it. On the one hand using constants reduces the error rate of typos. On the other hand it makes the function dependent on some external definitions.
I think this depends on the case and the overall need for scalability. Anyway, I tend to favour constants from an aesthetic point of view.
It does not use break
statements
The break
is something like the spaghetti-code's topping to me. It "breaks" control flow in an unnatural way, where it jumps like a goto
to an invisibly tagged point after the switch
block.
However, since there is no need to execute code after the switch
block we can safely return the result of the function called in the case
branch.
It throws and error as default
Defining a default
and not throwing an error is like writing an if-else
logic with multiple if-conditions - you can't control the reason why it branched into the else
.
The same thing applies for default
. The type
value could by anything, ranging from undefined
, null
or a totally different type.
To indicate such an undefined state it is therefore conclusive to throw an error here and examine the source of this uncovered type
value.
What do you think?
Note, that this has derived from my personal experience, mixed with what I picked up from readings or observed from other projects (for example the usage of constants in branching logic is something I have learned when working with Java).
If you still think there is something flawed in this concept and switch
should just be banned from the language set, please let me know.
Top comments (2)
Nice pont of view. Another way that I do the same thing but not use switch is:
Was just about to point the same thing. I feel switch statements are obsolete when you got bracket notation that allows you to do the same thing in a much simpler way.