Skip to main content

Command Palette

Search for a command to run...

Kotlin Code Smell 23 - Singletons

One Too Many: The Slippery Slope of Singleton Patterns

Updated
2 min read
Kotlin Code Smell 23 - Singletons
Y

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

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

Kotlin Code Smells

Part 14 of 36

In this series, we will see several symptoms and situations that make us doubt the quality of our development. We will present possible solutions. Most are just clues. They are no hard rules.

Up next

Kotlin Code Smell 22 - Accidental Methods on Business Objects

Bloated Cars & Code: When Your Objects Carry Too Much Junk in the Trunk

More from this blog

Yonatan Karp-Rudin | kotlin for backend developer skills | java for backend developer skills | SpringBoot | Tutorials

57 posts

Experienced Senior Software Engineer passionate about functional programming & Kotlin. Excels in app development, optimization, and team collaboration. Let's create something amazing!