Last active
June 10, 2019 12:08
-
-
Save markmaynard/16052894d9dd675d3b95f7f613d6aa36 to your computer and use it in GitHub Desktop.
Generic Singleton Factory attempt 2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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