Как элегантная пара родительского и дочернего тегов в определении DSL в Kotlin

Используя 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() } 

Solutions Collecting From Web of "Как элегантная пара родительского и дочернего тегов в определении DSL в Kotlin"

После некоторых экспериментов я нашел разумное элегантное решение, извлеченное из примера в Интернете.

Конфигурация находится в классе конфигурации. Вместе с забавным главным образом это выглядит так:

 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}'") } } } 

Наслаждайтесь!