如何在Kotlin DSL定义中优雅地结合父母和孩子标记

时间:2017-08-15 16:17:47

标签: kotlin dsl

使用Kotlin我想定义一个DSL来初始化一个结构。如您所见,我想创建一组模块,每个模块都有许多字段。

class DSL : ModuleDSL() {

    init {

        module(Module("myModule")) {
            field("Test", "Test")
            field("Test", "Test")
            field("Test", "Test")
        }

        module(Module("myOtherModule")) {
            field("Test", "Test")
            field("Test", "Test")
            field("Test", "Test")
        }
    }
}

每个字段都必须连接到模块。 Whit在闭包中包含字段定义,afaik我没有对模块的引用。在模块及其字段之间创建连接的最佳方式是什么。

为了完整性,这些是其他类

open class ModuleDSL {

    fun field(fieldname: String, attributename: String) {
        println("${fieldname} is named ${attributename}")
    }

    fun module(module: Module, function: () -> Unit) {
        function.invoke()
    }

    fun createModel() {
        println("Create my model")
    }

}

class Module(name: String) {

    init {
        println("Create entity ${name}")
    }
}

fun main(args: Array<String>) {

    val myModuleDsl = DSL()

    myModuleDsl.createModel()

}

1 个答案:

答案 0 :(得分:0)

经过一些实验,我找到了一个合理的优雅解决方案,从互联网上的一个例子中提取。

配置位于配置类中。与fun main一起看起来像这样:

class MyModuleConfigurator : ModuleConfigurator() {

    fun bld() =
        module("MyModule1") {
            +"Text"
            field {
                +"Fieldtext 1"
            }
            field {
                +"Fieldtext 2a"
                +"Fieldtext 2b"
                form("form in field 2")
            }
            form("Form 1")
        }

}

fun main(args: Array<String>) {
    val myHTML = MyModuleConfigurator()
    myHTML.bld()
}

配置类派生自实现配置标记的主类。有关其他信息,请参阅注释:

open class ModuleConfigurator {

    // Triggers on 'module' keyword
    protected fun module(name: String, init: Module.() -> Unit) {
        val module = Module(name)
        module.init()

        // Add your post DSL processing operation here
        println(module.toString())
    }

    class Module(val name: String) {

        // Triggers on 'field' keyword in the 'module'
        fun field(init: Field.() -> Unit) = initTag(this, init)

        // Triggers on 'form' keyword
        fun form(name: String) {
            println("form with name ${name} in module ${this.name}")
        }

        // Triggers on + keyword
        operator fun String.unaryPlus() {
            val textElement = ModuleConfigurator.TextElement(this)
            println("Module unary plus (name = ${textElement})")
        }

        // Other module internals

        private fun initTag(module: Module, init: Field.() -> Unit) {
            val myField = Field(module)
            module.add(myField)
            myField.init()
        }

        private fun add(myField: Field) {
            // Hook to add field to module
        }

        override fun toString(): String {
            return "Module(${name})"
        }

    }

    // Helper to unwrap the string in the Unary Plus
    class TextElement(val text: String) {
        override fun toString(): String {
            return "$text"
        }
    }

    class Field(val module: ModuleConfigurator.Module) {

        operator fun String.unaryPlus() {
            val textElement = ModuleConfigurator.TextElement(this)
            println("Field unary plus in module ${module.toString()}, text = '${textElement}'")
        }

    }

}

享受!