首页 > 解决方案 > Kotlin DSL:几个域和它们之间的引用

问题描述

我学习 kotlin DSL 并尝试描述我的领域对象。例如,我在 github ( VillageDSL ) 上阅读了非常简单的示例,发现它们非常有用:

val v = village {
    house {
        person {
            name = "Emily"
            age = 31
        }
        person(name = "Hannah") {
            age = 27
        }
        person("Alex", 21)
        person(age = 17, name = "Daniel")
    }
}

或者

val v = village containing houses {
    house with people {
        "Emily" age 31
        "Hannah" age 27
        "Alex" age 21
        "Daniel" age 17
    }
}

和互联网上的其他例子。

所有这些示例都展示了如何在单个空间中描述单个域。但真正的域更复杂,它们之间有联系。我希望 DSL 用户能够通过自动完成、验证等从另一个 DSL 引用对象。

I. 例如,我有字典:

val books = books {
  book {
    isbn = "0321712943"
    name = "Domain-Specific Languages"
  }
  book {
    isbn = "9781680507935"
    name = "Programming DSLs in Kotlin"
  }
}

我想参考这些书:

val shelf = shelf {
  book("9781680507935")
  book("0321712943")
}

我可以在运行时进行这样的验证:

data class Shelf(val books: List<Book> = listOf()): Populatable {
  override fun populate(bookDictionary: Books) {
    val dictionaryBooks = bookDictionary.books
    books.forEach {
      val book = checkNotNull(dictionaryBooks[it.isbn]) {
        "ISBN [${it.isbn}] was not found in book dictionary"
      }
      it.name = book.name
    }
  }
}

整个模型将保持一致,但在设计时用户必须手动输入 ISBN ( $.shelf.book.isbn)。

二、另一种变体。我在字典中这样描述每一本书:

val book0321712943 = book {
    isbn = "0321712943"
    name = "Domain-Specific Languages"
}
val book9781680507935 = book {
    isbn = "9781680507935"
    name = "Programming DSLs in Kotlin"
  }
}

这个变体对管理这本词典的用户不太友好,但对参考这些书的用户更友好:

val shelf = shelf {
  book(Books.book9781680507935)
  book(Books.book0321712943)
}

可以在设计时使用自动完成和验证工作。

三、我可以想象一些组合变体:字典像变体“I”($.books.book)一样管理,然后是代码生成的结构,如变体“II”(val book0321712943 = ... val book9781680507935 ...)。这种代码生成也是手动/中间操作,它不允许在没有项目构建等情况下立即使用字典实体。

所有这些变体都有明显的缺点......

所以我的问题是关于描述相关 DSL 的最佳实用方法和模板。

标签: kotlindslkotlin-dsl

解决方案


推荐阅读