Koin:如何降低模块的可见性(私有非导出模块)

问题描述 投票:0回答:3

我正在使用Koin 3.2,它具有新的模块包含功能。在官方文档中,在讨论模块链接策略时,有这样一段话:

需要注意的一个重要细节是,您也可以使用 include 来添加内部和私有模块 - 这使您可以灵活地在模块化项目中公开哪些内容。

这正是我所需要的,但我在文档的其他地方找不到如何设置仅为父模块提供依赖项的“私有”模块,以便这些子依赖项不可用于注入。例如:

class SomeNonInjectableClass
class SomeInjectableClass(private val sni : SomeNonInjectableClass)

val privateModule = module {
    singleOf(::SomeNonInjectableClass)
}

val publicModule = module {
    includes(privateModule)
    singleOf(::SomeInjectableClass)
}

在我的主应用程序中,我仅列出公共模块,但 Koin 自动提供所有包含的模块:

startKoin{
    androidLogger()
    androidContext(this@Main)
    modules(publicModule)
}

现在开发人员可以从任何活动中执行此操作:

val foo : SomeInjectableClass by inject() //Ok
val bar : SomeNonInjectableClass by inject() //I don't want this

我希望开发人员无法从私有模块注入不可注入的类。类似于 Dagger 2 的

@NonInjectable
标记限定符。

这可能吗?还是我应该使用经典 DSL 手动构建我的定义?

android kotlin dependency-injection koin
3个回答
1
投票

据我了解,这个新的

includes
功能只是使整个模块定义
private
internal
成为可能,不允许您将它们包含到此范围之外的模块中。我想它更多地与控制大型模块化项目中 Koin 模块的创建有关,而不是能够控制开发人员可以或不能注入什么。

private val privateModule = module {
   singleOf(::SomeNonInjectableClass)
} 

我可以理解,在跨不同 kotlin 模块创建大型 koin 模块组合时,如何将其设为私有范围可以避免一些混乱(特别是私有模块内有很多定义),但同时我也感到沮丧似乎不可能实现你想做的事情。


1
投票

澄清一下:

includes
运算符允许使用给定模块加载其他模块。

如果您想避免共享内部实现,请检查您的 Gradle 模块暴露情况。例如,共享接口即可。


0
投票

我试图做同样的事情,并找到了一些答案。

1.使用范围

import org.koin.core.context.startKoin
import org.koin.core.qualifier.named
import org.koin.dsl.module

val module = module {
    scope(named("private")) {
        scoped(named("PRIVATE_KEY")) { "ABCD1234" }
    }

    single(named("result")) {
        val scope = getKoin().getOrCreateScope("private", named("private"))
        "Key is ${scope.get<String>(named("PRIVATE_KEY"))}"
    }
}


fun main() {
    val koinApp = startKoin {
        modules(module)
    }

    val koin = koinApp.koin
    val result = koin.get<String>(named("result"))
    println("Result: $result") // -> Result: Key is ABCD1234

    val keyVisible = koin.getOrNull<String>(named("PRIVATE_KEY")) != null
    println("Key visibility: $keyVisible") // -> Key visibility: false
}

您可以通过将依赖项移动到不同的范围来隐藏依赖项。 然而,这是默认的,因为

koinApp.get()
正在尝试从根范围获取值。 同样,您必须在外部模块内部定义内部依赖模块。

这是我发现的更好的方法。

2.哈克方式

val privateModule = module {
    single(named("1")) { 10 }
    single(named("2")) { 20 }
}

val publicModule = module {
    single(named("result")) {
        val a = privateModule.get<Int>(named("1"))
        val b = privateModule.get<Int>(named("2"))
        a + b
    }
}

context(Scope)
@OptIn(KoinInternalApi::class)
inline fun <reified T> Module.get(
    qualifier: Qualifier,
    noinline parameters: ParametersDefinition? = null,
): T {
    val scope = this@Scope
    val instanceFactory =
        mappings[indexKey(T::class, qualifier, named("_root_"))] ?: error("No definition found from $qualifier")

    val context = InstanceContext(scope.logger, scope, parameters?.invoke())
    return instanceFactory.get(context) as T
}


fun main() {
    val koinApp = startKoin {
        modules(publicModule)
    }

    val koin = koinApp.koin
    val result = koin.get<Int>(named("result"))
    println("Result: $result") // -> Result: 30

    val internalVisible = koin.getOrNull<Int>(named("1")) != null
    println("Internal visibility: $internalVisible") // Internal visibility: false
}

它使用 Kotlin 的实验性 Context 功能来实现美观的代码。 如果您不想使用上下文,可以使用类似

use(privateModule).get()
的语法。

解决方案选择内部使用 Koin API 并对根范围进行硬编码,但它还算有效。 不使用作用域意味着不可能从外部解决私有依赖关系。您只需将私有模块定义为私有 val 即可。

希望这能帮助那些想做同样事情的人!

© www.soinside.com 2019 - 2024. All rights reserved.