An example of a singleton pattern in Kotlin.
Table of contents for design patterns in Kotlin
In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one “single” instance. This is useful when exactly one object is needed to coordinate actions across the system. The term comes from the mathematical concept of a singleton. Wikipedia
Example 1:
SingletonHolder
open class SingletonHolder<T: Any, in A>(creator: (A) -> T) {
private var creator: ((A) -> T)? = creator
@Volatile protected var instance: T? = null
fun getInstance(arg: A): T {
val checkInstance = instance
if (checkInstance != null) {
return checkInstance
}
return synchronized(this) {
val checkInstanceAgain = instance
if (checkInstanceAgain != null) {
checkInstanceAgain
} else {
val created = creator!!(arg)
instance = created
creator = null
created
}
}
}
}
DataBaseConnect
class DataBaseConnect private constructor(private val url: String) {
fun connect(): String = url
companion object : SingletonHolder<DataBaseConnect, String>(::DataBaseConnect)
}
Usage:
fun main() {
val dbA = DataBaseConnect.getInstance("connect url A")
val dbB = DataBaseConnect.getInstance("connect url B")
dbA.apply {
println("${connect()} ${hashCode()}")
}
dbB.apply {
println("${connect()} ${hashCode()}")
}
}
Result
connect url A 434091818
connect url A 434091818
Example 2:
Singleton
open class Singleton<T : Any>(creator: () -> T) {
private var creator: (() -> T)? = creator
@Volatile
protected var instance: T? = null
fun get(): T {
val checkInstance = instance
if (checkInstance != null) {
return checkInstance
}
return synchronized(this) {
val checkInstanceAgain = instance
if (checkInstanceAgain != null) {
checkInstanceAgain
} else {
val created = creator!!()
instance = created
creator = null
created
}
}
}
companion object {
fun <T : Any> singletonOf(block: () -> T): Singleton<T> = Singleton(block)
}
}
ExternalClass
class ExternalClass(val data: String)
Usage:
fun main() {
val holder = singletonOf { ExternalClass("config data") }
val dbC = holder.get()
val dbD = holder.get()
dbC.apply {
println("$data ${hashCode()}")
}
dbD.apply {
println("$data ${hashCode()}")
}
}
Result
config data 985922955
config data 985922955