Skip to content

Instantly share code, notes, and snippets.

@gumil
Last active December 19, 2019 14:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gumil/c0d1cc404f7f19b4664cc044683847cc to your computer and use it in GitHub Desktop.
Save gumil/c0d1cc404f7f19b4664cc044683847cc to your computer and use it in GitHub Desktop.
Simple DI
import kotlin.reflect.KProperty
internal class Component(modules: List<Module>, private val parentComponent: Component? = null) {
private val providers: Map<Class<*>, Provider<*>> = hashMapOf<Class<*>, Provider<*>>().apply {
modules.forEach { putAll(it) }
}
inline fun <reified T> get() = get(T::class.java)
private fun <T> get(clazz: Class<T>): T {
return getProvider(clazz).get(this)
}
fun <T> getNullable(clazz: Class<T>): T? {
return getProviderOrNull(clazz)?.get(this)
}
private fun <T> getProviderOrNull(clazz: Class<T>): Provider<T>? {
return parentComponent?.getProviderOrNull(clazz) ?: providers[clazz] as? Provider<T>
}
private fun <T> getProvider(clazz: Class<T>): Provider<T> {
return getProviderOrNull(clazz) ?: throw IllegalStateException("No definition found for ${clazz.simpleName}")
}
}
internal interface HasComponent {
val component: Component
}
internal inline fun <reified T> HasComponent.inject(): Injected<T> {
return Injected { component.get<T>() }
}
internal inline fun <reified T> HasComponent.injectNullable(): Injected<T?> {
return Injected { component.getNullable(T::class.java) }
}
internal class Injected<T>(private val initializer: () -> T) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return initializer()
}
}
internal class Module(private val providers: Map<Class<*>, Provider<*>> = hashMapOf()) :
Map<Class<*>, Provider<*>> by providers
internal class ModuleBuilder {
val providers: MutableMap<Class<*>, Provider<*>> = hashMapOf()
inline fun <reified T> bind(noinline block: Component.() -> T) {
providers[T::class.java] = Provider(block)
}
}
internal fun module(block: ModuleBuilder.() -> Unit): Module {
val builder = ModuleBuilder()
builder.block()
return Module(builder.providers)
}
internal data class Provider<T>(private val create: (Component) -> T) {
private var original: T? = null
fun get(component: Component): T = original ?: run {
val get = create(component)
original = get
get
}
}
fun createCommonModule(): Module {
return module {
bind { Repository(get(), get()) }
bind { Database(get()) }
bind { Network() }
}
}
fun init() {
Component(createCommonModule())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment