Skip to content

Instantly share code, notes, and snippets.

@markmaynard
Last active June 10, 2019 12:08
Show Gist options
  • Save markmaynard/16052894d9dd675d3b95f7f613d6aa36 to your computer and use it in GitHub Desktop.
Save markmaynard/16052894d9dd675d3b95f7f613d6aa36 to your computer and use it in GitHub Desktop.
Generic Singleton Factory attempt 2
import kotlin.reflect.KClass
import kotlinx.coroutines.*
fun main() {
println("Welcome to Thingville, population 1")
val factory = ThingFactory(Thing1::class)
factory?.run() // "Run Fast!"
factory?.play() // "Run Hard!"
val factory2 = ThingFactory(Thing2::class) // Run Time Error: Already created as type: class Thing1
val factory3 = ThingFactory(NoThing::class) // Compile Time Error: None of the following functions can be called with the arguments supplied: public constructor ThingFactory(clazz: ThingFactory.ThingType) defined in ThingFactory public inline fun <reified T : SomeThing> ThingFactory(clazz: KClass<???>): ThingFactory?
// Thread safe check
runBlocking {
val deferred = (1..20).map { n ->
GlobalScope.async {
val r = (1..10).random()
println("In ${n}")
try {
when {
r % 2 == 0 -> {
println("Create ${n} Thing1")
ThingFactory(Thing1::class)
}
else -> {
println("Create ${n} Thing2")
ThingFactory(Thing2::class)
}
}
} catch(e: RuntimeException) {
println("Error ${n} : ${e.message}")
}
println("Out ${n}")
}
}
deferred.awaitAll()
}
}
sealed class SomeThing {
abstract fun run()
abstract fun play()
}
class Thing1: SomeThing() {
override fun run() = println("Run Fast!")
override fun play() = println("Play Hard!")
}
class Thing2: SomeThing() {
override fun run() = println("Run Silly!")
override fun play() = println("Play Soft!")
}
class NoThing {}
interface ClassBackedEnum {
fun toClass(): Any
}
inline fun <reified T: SomeThing> ThingFactory(clazz: KClass<T>): ThingFactory? =
when(clazz) {
Thing1::class -> ThingFactory(ThingFactory.ThingType.Thing1Type)
Thing2::class -> ThingFactory(ThingFactory.ThingType.Thing2Type)
else -> throw RuntimeException("Unknown type: $clazz")
}
class ThingFactory private constructor() {
enum class ThingType(): ClassBackedEnum {
Thing1Type {
override fun toClass() = Thing1::class
},
Thing2Type {
override fun toClass() = Thing2::class
}
}
constructor (clazz: ThingType): this() {
if (myThing == null) {
when(clazz.toClass()) {
Thing1::class -> myThing = Thing1()
Thing2::class -> myThing = Thing2()
}
} else {
if (myThing?.javaClass?.kotlin != clazz.toClass()) {
throw RuntimeException("Already created as type:${myThing?.javaClass?.kotlin}")
}
}
}
fun run() {
myThing?.run()?: throw RuntimeException("Singleton not instantiated")
}
fun play() {
myThing?.play()?: throw RuntimeException("Singleton not instantiated")
}
companion object {
@Volatile var myThing: SomeThing? = null
@Synchronized set(value: SomeThing?) {
val i = myThing
if (i != null) {
field = i
} else {
field = value
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment