Kotlin Code Smell 23 - Singletons

Kotlin Code Smell 23 - Singletons

One Too Many: The Slippery Slope of Singleton Patterns

Problem

  • Coupling

  • Testability

  • Accidental implementation problems

  • Multi-threading issues

  • Static methods polluting

  • Object creation contract violation

  • Bijection mismatch

  • Memory issues

  • Premature Optimization

Solution

  1. Avoid singletons altogether.

  2. Use contextual unique objects instead of relying on a global instance.

  3. Benchmark object creation to ensure performance is not adversely affected.

Examples

  • Database Access

  • Globals

  • Loggers

  • Helper classes

Sample Code

Wrong

// God is the archetypal singleton example
class God {
    // In Kotlin, the companion object is always a singleton
    companion object {
        private var instance: God? = null

        fun getInstance(): God {
            if (instance == null) {
                instance = God()
            }

            return requireNotNull(instance)
        }
    }
}

// Why should we be aware of getInstance when creating an object?
val jewishGod = God.getInstance()

Right

interface Religion {
    // Define behavior
}

class God {
    // There can be as many as you wish
}

class PolytheisticReligion(private val gods: Collection<God>) : Religion
class MonotheisticReligion(private val godAllMighty: God) : Religion

// According to Judaism, there's only one God,
// but this does not hold in other religions.
// Under this context, God is unique. We cannot create or change
// a new one.
val jewishGod = God()
val judaism = MonotheisticReligion(jewishGod)

val jupiter = God()
val saturn = God()
val mythologicalReligion = PolytheisticReligion(listOf(jupiter, saturn))

// Gods are unique (or not) according to context.
// We can create test religions with or without unicity.
// This is less coupled since we break the direct reference to the
// God class.
// The God class has the single responsibility to create gods, not
// to manage them.

Conclusion

The use of singletons is a historical mistake that has already been acknowledged by the community. Nevertheless, lazy developers bring it up again and again. We need to reach a consensus on its drawbacks and strive for better design patterns.

Credits

Did you find this article valuable?

Support Yonatan Karp-Rudin by becoming a sponsor. Any amount is appreciated!