Kotlin Code Smell 14 - Anonymous Functions Abusers
Closures in Check: Enhancing Code Maintainability
Table of contents
TL;DR: Avoid excessive use of closures and functions. Encapsulate them within objects.
Problems
Maintainability
Testability
Code Reuse
Implementation Hiding
Debugging
Solutions
Wrap functions/closures
Reify algorithms using a method object / Strategy pattern
Sample Code
Wrong
fun sortFunction(
list: MutableList<Int>,
fn: (Int, Int) -> Boolean
) {
for (i in list.indices) {
for (j in 0 until list.size - i - 1) {
if (fn(list[j], list[j + 1])) {
val temp = list[j]
list[j] = list[j + 1]
list[j + 1] = temp
}
}
}
}
fun main() {
val scores = mutableListOf(9, 5, 2, 7, 23, 1, 3)
sortFunction(scores) { a, b -> a > b }
}
Right
class ElementComparator {
fun greaterThen(firstElement: Int, secondElement: Int) =
firstElement > secondElement
// This is just an example. With more complex objects,
// this comparison might not be so trivial...
}
class BubbleSortStrategy(
private val elements: MutableList<Int>,
private val comparator: ElementComparator
) {
// We have a strategy; we can unit test it, change it to a
// polymorphic implementation, or benchmark different algorithms, etc.
fun sort() {
for (i in elements.indices) {
for (j in 0 until elements.size - i - 1) {
if (comparator.greaterThen(elements[j], elements[j + 1])) {
swap(j)
}
}
}
}
private fun swap(index: Int) {
val temp = elements[index]
elements[index] = elements[index + 1]
elements[index + 1] = temp
}
}
fun main() {
val scores = mutableListOf(9, 5, 2, 7, 23, 1, 3)
BubbleSortStrategy(scores, ElementComparator()).sort()
}
Conclusion
Humans read code, and while software can handle anonymous functions, maintainability suffers when multiple closures are used. By extracting functionality into objects, we can improve code reuse and maintainability.