Overview
In data-oriented programming, the usage of switch-like statements (lengthy, cascading if statements or switch/case) should be relatively rare. One such switch usually executes code scattered around the code base and should usually be replaced with a polymorphism solution.
This smell was phrased initially as Switch Statement by Fowler and Beck back in 1999 [1]. One year later, Mäntylä noted that such a name is misleading since switch statements do not necessarily imply a code smell but rather just in the situation when they are used instead of a viable polymorphism solution. [2] Fowler, 14 years later, in his newest book in 2018, changed the name, suggesting Repeated Switching, agreeing that, fortuitously due to the way he initially phrased it, conditional statements got a bad reputation, while he never unconditionally opposed the existence of all if-s and switch-es. [3] In the “Clean Code” by Robert Martin, the smell was called “Prefer Polymorphism to if/else or switch/case”, which is lengthy, but it hits the nail on the head. [4]
I provide a typical example of this issue on an Exporter class with different file formats. A new elif has been added for each new feature instead of using an Object-Oriented solution like the Factory Method.
Keeping a class focused on a single concern is vital to make it more robust. Martin Fowler defines responsibility as a reason to change, concluding that “A class should have only one reason to change.” [3]. An example that violates this would be a class that prints a table that handles both the contents of the cells and the styling of the table.
Another situation, which is not explicitly mentioned in the sources, would be a nested Try and Except/Catch “checklist”, where numerous error-catching blocks are used instead of one generalized for the situation at hand.
Causation
The most common way such a smell can be created is when a conditional switch behavior is used instead of creating a new class. The first time it happens is not yet a “scent-ish” smell, but this immediately becomes a saturated red flag as soon as it is done the second time [5]. Logic blocks grew over time more extensive, and no one bothered to implement an Object-Oriented Programming style alternative like decorator, strategy, or state. It was easier to add another else if.
Problems
The class should be open for extension but closed for modification
If statement blocks make things harder to understand as they require to think about all the possible paths the logic can go.
Most likely, there is a way to execute one method on a polymorphic class instead of switching cases.
Each subsequent if branch needs each subsequent test.
Example
1class Exporter:
2 def export(self, export_format: str):
3 if export_format == 'wav':
4 self.exportInWav()
5 elif export_format == 'flac':
6 self.exportInFlac()
7 elif export_format == 'mp3':
8 self.exportInMp3()
9 elif export_format == 'ogg':
10 self.exportInOgg()Refactoring
- Use a Guard Clause
- Extract Conditional
- Replace with Polymorphism
- Use Strategy Pattern
- Use Null Object
- Use Functional Programming Based Solution
Sources
- ORIGIN
- UPDATE2018 · ISBN 978-0134757681
- PARENTAGE1999 · ISBN 978-0201485677