Kotlin Code Smell 26 - if-else/when statements
Code Evolution: Conquering Complexity with Patterns

I've started to work as a software engineer at 2014, however, I started to write code at high-school.
My first language was Assembly, but still, I fall in love with the possibilities to make the computer to do as you wish, shortly after that I started to write in C.
Later on I studied a practical engineering in electricity, and during this time discovered that I preferred much more writing code than design electrical components.
As a result of this understanding I decided to switch and study bachelor degree in computer science in Reichman university, where the focus was of the Java language.
Today I'm working at SumUp using Kotlin, SpringBoot & Micronaut, Cassandra and Kafka
First programming lesson: Control structures. Senior developer lesson: avoid them.
Problems
- Too many decisions together
- Coupling
- Duplicated code
- Violation of Open/Closed Principle.
- A new condition should not change the main algorithm.
- Nulls
Solutions
- Polymorphism
- Create hierarchies/compose objects following the Open closed principle.
- Use State pattern to model transitions.
- Use Strategy Pattern/Method Object to choose for branches.
Examples
- Discrete Values
- State transition
- Algorithm choice.
Sample Code
Wrong
fun convertToMp3(source: Path, mimeType: String) =
when(mimeType) {
"audio/mpeg" -> convertMpegToMp3(source)
"audio/wav" -> convertWavToMp3(source)
"audio/ogg" -> convertOggToMp3(source)
// Lots of new if-else cases
else -> throw IllegalArgumentException("Unknown mime type")
}
fun convertMpegToMp3(source: Path) = println("Convert from mpeg")
fun convertWavToMp3(source: Path) = println("Convert from wav")
fun convertOggToMp3(source: Path) = println("Convert from ogg")
Right
val registeredConverters = mapOf(
"audio/mpeg" to ::convertMpegToMp3,
"audio/wav" to ::convertWavToMp3,
"audio/ogg" to ::convertOggToMp3,
// Lots of other converters
)
fun convertToMp3(source: Path, mimeType: String) =
registeredConverters[mimeType]
?.let { converter -> converter(source) }
?: throw IllegalArgumentException("No converter found")
fun convertMpegToMp3(source: Path) = println("Convert from mpeg")
fun convertWavToMp3(source: Path) = println("Convert from wav")
fun convertOggToMp3(source: Path) = println("Convert from ogg")
After Further Refactoring (Using Objects)
interface Mp3Converter {
fun convertToMp3(source: Path)
}
class ConvertMpegToMp3 : Mp3Converter {
override fun convertToMp3(source: Path) =
println("Convert from mpeg")
}
class ConvertWavToMp3 : Mp3Converter {
override fun convertToMp3(source: Path) =
println("Convert from wav")
}
class ConvertOggToMp3 : Mp3Converter {
override fun convertToMp3(source: Path) =
println("Convert from ogg")
}
// Many more converters
val registeredConverters = mapOf(
"audio/mpeg" to ConvertMpegToMp3(),
"audio/wav" to ConvertWavToMp3(),
"audio/ogg" to ConvertOggToMp3(),
// Lots of other converters
)
fun convertToMp3(source: Path, mimeType: String) =
registeredConverters[mimeType]
?.convertToMp3(source)
?: throw IllegalArgumentException("No converter found")
Conclusion
Excessive use of if-else/when statements in Kotlin can lead to code smells, making the code difficult to maintain and understand. To avoid these issues, it is essential to utilize design patterns such as polymorphism, state patterns, and strategy patterns.
You can almost always replace the if-else/when statement with a map implementation.




