Sorry to rain on your parade. I know that you just refactored that series of if-else statements into one switch statement and you're feeling like you did your good deed for the day.
Take a moment to rest on your laurels before I tear your laurels to shreds.
Go ahead, I’ll wait.
Okay, now let's get down to business.
Saying the same thing a different way
The problem with changing if-else statements into a switch statement is that nothing really has changed besides the dialect in which it was said.
It’s all apples!
Now, switch is slightly better than if-else for maintenance purposes, but not much better. Both constructs (switch and if-else) try to represent some form of data (a mapping) as code.
Now data and code are not clearly delineated. But, in general we have a concept of what constitutes data and what constitutes code.
Data is some information that does not contain in itself logic.
Code is some information that is primarily instructions and logic.
For this reason, I say that switch statements and if-else statements are both trying to treat data as logic. They are both code constructs that mix in data elements with logic elements and do not clearly separate the two, although a switch statement does a slightly better job of it.
Consider the most basic form of an if-else and switch statement. The form in which you directly map one piece of data to another piece of data.
If “A” then return “kittens”, If “B” then return “puppies”…
Switch (data1), case “A”: return “kittens”, case “B”: return “puppies”…
When I rewrite it in this form, the parallel becomes much more clear. We can also very easily separate logic from data.
- Map something of dataset1 to something of dataset2.
- Return a mapped value.
The problem with either of these forms, (the switch or the if-else), is that both of them tend to mix logic and data together.
Why is it bad to mix logic and data?
Let me ask you a question. Which is more likely to change?
Hopefully it is data and not logic.
Consider the Single Responsibility Principle (SRP.)
A module should have one and only one reason to change. Ideally, we want to separate out logic from data so that we can change the two independently.
Sticking with our contrived example, suppose we wanted to make “A” map to “dinosaurs” instead of “kittens,” or we want to add more mappings. We should be able to do this in a way that does not interfere with the logic at all or add new logic.
If we have an if-else structure, we will have to add more if-else constructs or change a data value that is inside of the method that is doing the logic. The same applies for a switch statement. We are not declaring our data one place and our logic another, they will be side by side, right next to each other in the code.
Suppose we want to change the logic so that instead of returning a value, we assign it to some variable, or reverse it. Again, we are faced with changing logic in multiple places, in the same module as the data.
Consider the case where we want to read the data from a file or some other data source? Is that possible with a switch or if-else structure? Not really, because the data is essentially “hard-coded” into the logic.
What can we do about it?
So, by now hopefully you have gotten the point that your refactoring of the if-else into a switch statement didn’t really solve the basic problem of mixing data and logic together.
But now you have another problem. You need to separate your data and logic so that they can change independently. You want to be a good steward of the SRP. You want to someday be able to read your data from a file or database, so that you won’t even have to recompile your code to change it.
On my next post, I will show you how to take switch statements and refactor them into a better form that separates data from logic.
I will talk about the different “forms” of the switch statement and why to choose a particular method of refactoring over another.