Singleton

This is the most popular single guy in the neighborhood. Everybody knows him, everybody talks about him, and anybody can find him easily.

Even people who will frown when other design patterns are mentioned will know it by name. At some point, it was even proclaimed an anti-pattern, but only because of its wide popularity. So, for those who are hearing about it for the first time, what is this pattern about? 

Usually, if you have an object, you can create as many of its instances as you want. Say, for example, you have the Cat class:

class Cat

You can produce as many of its instances (cats, to be precise), as you want:

val firstCat = Cat()
val secondCat = Cat()
val yetAnotherCat = Cat()

And there's no problem with that. 

What if we wanted to disallow such behavior? Clearly, we have to create an object in some way for the first time. But from the second time on, we need to recognize that this object was initialized once already, and returns its instance instead. That's the main idea behind being a Singleton.

In Java and some other languages, this task is quite complex. It's not enough to simply make the constructor private and remember that the object was initialized at least once already. We also need to prevent race conditions, where two separate threads try to initialize it exactly at the same time. If we allowed that, it would break the entire concept of a Singleton, as two threads would hold references to two instances of the same object.

Solving this problem in Java requires doing one of the following:

  • Accepting that a Singleton will initialize eagerly when your application starts, and not when it is first accessed
  • Writing some smart code to prevent such race conditions and still stay performant
  • Using a framework that already solves it

Kotlin just introduces a reserved keyword for that. Behold, an object as follows:

object MySingelton{}
You don't need curly brackets there. They're just for visual consistency.

This combines declaration and initialization in one keyword. From now on, MySingleton can be accessed from anywhere in your code, and there'll be exactly one instance of it.

Of course, this object doesn't do anything interesting. Let's make it count the number of invocations instead:

object CounterSingleton {
private val counter = AtomicInteger(0)

fun increment() = counter.incrementAndGet()
}

We won't test it for thread safety yet this is a topic that will be covered in Chapter 8, Threads and Coroutines, which deals with threads. For now, we test it only to see how we call our Singleton:

for (i in 1..10) {
println(CounterSingleton.increment())
}

This will print numbers between 1 and 10, as expected. As you can see, we don't need the getInstance() method at all.

The object keyword is used for more than just creating Singletons. We'll discuss it in depth later.

Objects can't have constructors. If you want some kind of initialization logic for your Singleton, such as loading data from the database or over the network for the first time, you can use the init block instead:

object CounterSingleton {

init {
println("I was accessed for the first time")
}
// More code goes here
}

It is also demonstrated that Singletons in Kotlin are initialized lazily, and not eagerly, as some could suspect from the ease of their declaration. Just like regular classes, objects can extend other classes and implement interfaces. We'll come back to this in Chapter 10, Idioms and Anti-Patterns.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.9.147