Thursday, August 29, 2019

Kotlin in Action: Answers to Chapter 11 Exercises

Here's the answers to the chapter 11 exercises.

Exercise: Family Tree

fun man(description: Man.() -> Unit): Man =
    Man().apply(description)

fun woman(description: Woman.() -> Unit): Woman =
    Woman().apply(description)

class Man: Person() {
    var wife: Woman? = null

    fun wife(description: Woman.() -> Unit): Woman =
        woman(description).also { woman ->
            wife = woman
            woman.husband = this
        }

    override fun toString(): String =
        toString("Man", "wife", wife?.name)
}

class Woman: Person() {
    var husband: Man? = null

    fun husband(description: Man.() -> Unit): Man =
        man(description).also { man ->
            husband = man
            man.wife = this
        }

    override fun toString(): String =
        toString("Woman", "husband", husband?.name)
}

sealed class Person() {
    val name = Name()
    var father:Man? = null
    var mother:Woman? = null
    val children = mutableListOf<Person>()

    fun name(description: Name.() -> Unit): Name =
        name.apply(description)

    fun father(description: Man.() -> Unit): Man =
        man(description).also { man ->
            father = man
            man.children.add(this)
        }

    fun mother(description: Woman.() -> Unit): Woman =
        woman(description).also { woman ->
            mother = woman
            woman.children.add(this)
        }

    fun son(description: Man.() -> Unit): Man =
        man(description).also { man ->
            children.add(man)
            man.addParent(this)
        }

    fun daughter(description: Woman.() -> Unit): Woman =
        woman(description).also { woman ->
            children.add(woman)
            woman.addParent(this)
        }

    protected fun addParent(parent: Person) {
        when (parent) {
            is Man -> {
                father = parent
                parent.wife?.let { mother ->
                    this.mother = mother
                    mother.children.add(this)
                }
            }
            is Woman -> {
                mother = parent
                parent.husband?.let { father ->
                    this.father = father
                    father.children.add(this)
                }
            }
        }
    }

    protected fun toString(
        typeName: String,
        spouseTypeName: String,
        spouseName: Name?
    ): String =
        "$typeName(" +
            "name=$name, " +
            "father=${father?.name}, " +
            "mother=${mother?.name}, " +
            "$spouseTypeName=$spouseName, " +
            "children=${children.map {it.name}}" +
        ")"
}

data class Name(var first:String="",
                var middle:String="",
                var last:String="")