用于收集的Kotlin DSL

时间:2018-10-19 15:15:49

标签: kotlin dsl

这是一个简单的数据结构House,其中包含Person的(可空)列表:

data class Person(val name: String,
                  val militaryYear: Int? = null,
                  val firstPregnantYear: Int? = null)
data class House(val persons: List<Person>?)

Person可能是男性或女性。男性的价值为militaryYear,女性的价值为firstPregnantYear

我想编写一个DSL来构建House对象,像这样:

val house = house {
  person("John") {
    militaryYear = 2003
  }
  person("Anne") {
    firstPregnantYear = 2010
  }
}

这就是我所做的:

data class Person(val name: String,
                  val militaryYear: Int? = null,
                  val firstPregnantYear: Int? = null) {

  class Builder(private val name: String) {
    var militaryYear : Int? = null
    var firstPregnantYear : Int? = null
    fun build(): Person = Person(name, militaryYear, firstPregnantYear)
  }
}
fun person(name: String , block: Person.Builder.() -> Unit) = Person.Builder(name).apply(block).build()



data class House(val persons: List<Person>?) {
  class Builder {
    var persons = mutableListOf<Person>()

    fun person(name: String , block: Person.Builder.() -> Unit) {
      persons.add(Person.Builder(name).apply(block).build())
    }

    fun build(): House = House(persons = persons.takeIf { it.isNotEmpty() })
  }
}

似乎可行,但同时禁止militaryYearfirstPregnantYear,例如:

val house = house {
  person("John") {
    militaryYear = 2003
  }
  person("Anne") {
    firstPregnantYear = 2010
  }
  person("Alien") {
    militaryYear = 2005
    firstPregnantYear = 2009
  }
}
println(house)

结果:

House(persons=[
Person(name=John, militaryYear=2003, firstPregnantYear=null)
, Person(name=Anne, militaryYear=null, firstPregnantYear=2010)
, Person(name=Alien, militaryYear=2005, firstPregnantYear=2009)
])

Alien具有militaryYearfirstPregnantYear,这是不正确的。

因此,我添加了另一个MaleBuilderFemaleBuilder

  class MaleBuilder(private val name: String) {
    var militaryYear = 0
    fun build(): Person = Person(name, militaryYear, null)
  }

  class FemaleBuilder(private val name: String) {
    var firstPregnantYear = 0
    fun build() : Person = Person(name , null , firstPregnantYear)
  }

fun male(name: String , block: Person.MaleBuilder.() -> Unit) = Person.MaleBuilder(name).apply(block).build()
fun female(name: String , block: Person.FemaleBuilder.() -> Unit) = Person.FemaleBuilder(name).apply(block).build()

然后DSL变为:

val house = house {
  male("John") {
    militaryYear = 2003
  }
  female("Anne") {
    firstPregnantYear = 2010
  }
}
println(house)

好,这是问题所在,male("John")female("Anne")未插入persons中。输出:

House(persons=null)

我认为问题来自于House的person()函数:

fun person(name: String , block: Person.Builder.() -> Unit) {
  persons.add(Person.Builder(name).apply(block).build())
}

定义的接收者为Person.Builder,但在代码中为MaleBuilderFemaleBuilder

如何解决这个问题?

完整代码:https://pastebin.com/1Q6D8rzx

-------更新-------

可以通过引入House.Builder.male()House.Builder.female()函数来解决:

fun male(name: String , block: Person.MaleBuilder.() -> Unit) {
  persons.add(Person.MaleBuilder(name).apply(block).build())
}

fun female(name: String , block: Person.FemaleBuilder.() -> Unit) {
  persons.add(Person.FemaleBuilder(name).apply(block).build())
}

但是我认为这不是一个好的解决方案。在实际示例中,可能有很多属性,并且有很多限制。有没有一种方法可以使用一个函数来接收它?

0 个答案:

没有答案