这是一个简单的数据结构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() })
}
}
似乎可行,但同时禁止militaryYear
和firstPregnantYear
,例如:
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
具有militaryYear
和firstPregnantYear
,这是不正确的。
因此,我添加了另一个MaleBuilder
和FemaleBuilder
:
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
,但在代码中为MaleBuilder
和FemaleBuilder
。
如何解决这个问题?
完整代码: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())
}
但是我认为这不是一个好的解决方案。在实际示例中,可能有很多属性,并且有很多限制。有没有一种方法可以使用一个函数来接收它?