从DSL块中的语句创建列表

问题描述 投票:1回答:1

为了构建用于验证的DSL,我正在寻找用接收器收集块/ lambda内的语句的方法。为了说明,这里是一个没有实际验证逻辑的最小示例:

data class Constraint(val hint: String)

class Validation(val constraints: List<Constraint>) {

    companion object {
        operator fun invoke(init: (ValidationBuilder.() -> Unit)): Validation {
            return ValidationBuilder().apply(init).build()
        }
    }

    class ValidationBuilder {
        private var constraints: MutableList<Constraint> = mutableListOf()

        operator fun Constraint.unaryPlus() {
            constraints.add(this)
        }

        fun build() = Validation(constraints)
    }
}

然后可以使用它来构建类似的验证

val validation = Validation {
    +Constraint("First constraint")
    val secondConstraintHint = "Second constraint"
    +Constraint(secondConstraintHint)
}

我想摆脱unaryPlus运算符并直接收集块中的单个语句,这些语句被评估为Constraint,以便我可以执行以下操作:

val validation = Validation {
    Constraint("First constraint")
    val secondConstraintHint = "Second constraint"
    Constraint(secondConstraintHint)
}

这有可能吗?

为了给出更多的上下文,我的目标实际结果将更像这样:

Validation<User> {
    User::firstName {
        val min = 2
        minLength(min) hint "Please provide a first name"
        maxLength(200) // uses default hint
    }
}
kotlin
1个回答
1
投票

好吧,似乎没有直接的解决方案,因为Kotlin无法处理未分配,返回或传递到任何地方的评估表达式结果。

一种可能的解决方法是使用为构建器定义的函数来模拟所需的构造函数:

class ValidationBuilder {
    /* ... */

    fun Constraint(name: String) = 
        full.qualified.name.of.Constraint(name).also(constraints::add)
}

不幸的是,这将要求您以这种方式复制要调用的所有签名。


UPD(回答评论):我认为用户定制DSL的惯用方法是为DSL构建器定义自己的扩展:

fun ValidationBuilder.nonEmptyText(min: Int = 1, max: Int = 65.536) = TODO()

如果来自DSL外部的Constraint是一个重要的用例,您可以使用特殊功能(例如fun ValidationBuilder.constraint(...))覆盖它,并让用户将其扩展名委托给它。

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