Это простая структура данных House
, которая содержит (обнуляемый) список Person
:
data class Person(val name: String,
val militaryYear: Int? = null,
val firstPregnantYear: Int? = null)
data class House(val persons: List<Person>?)
Person
может быть мужской или женский.Male имеет значение militaryYear
, Female имеет значение 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)
Я думаю, что проблема связана с функцией человека () Хауса:
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())
}
Но я думаю, что это не очень хорошее решение.В примере с реальным миром может быть много свойств и много ограничений.Есть ли способ использовать одну функцию для ее получения?