Kotlin DSL builders generalization

Viewed 84

While writing DSL in Kotlin for an API with lots of classes I'd encountered an issue. Code seemed completely boilerplate to me. Is there a way to generalize code in DSL builders?

Suppose we have an API:

class Robot(val name: String, val head: Head, val body: Body, val hands: Hands, ...)

class Head(val mouth: Mouth, ... )

class Body(val material: Material, ... )

class Hands(...)

class Mouth(...)

class Material(...)

The classes dependency tree could be much larger than that.

DSL for this api would look something like:

fun robot(name: String, settings: RobotBuilder.() -> Unit) = RobotBuilder(name).apply(settings).build()


interface Builder<T> {
    fun build(): T
}


@RobotDsl
class RobotBuilder(val name: String) : Builder<Robot> {
    private var head: Head = DefaultBotParts.head
    private var body: Body = DefaultBotParts.body
    private var hands: Hands = DefaultBotParts.hands

    ...

    fun head(initHead: HeadBuilder.() -> Unit) {
        head = HeadBuilder().apply(initHead).build()
    }

    fun body(initBody: BodyBuilder.() -> Unit) {
        body = BodyBuilder().apply(initBody).build()
    }

    fun hands(initHands: HandsBuilder.() -> Unit) {
        body = HandsBuilder().apply(initHands).build()
    }

    ...    

    override fun build(): Robot = Robot(name, head, hands)
}

@RobotDsl
class HeadBuilder : Builder<Head> {
    private var mouth: Mouth = DefaultBotParts.mouth

    fun mouth(init: MouthBuilder.() -> Unit) {
       mouth = Mouth().apply(init).build()
    }

    override fun build(): Robot = Head(mouth)
}

@RobotDsl
class MouthBuilder : Builder<Mouth> {
    //its properties and dsl functions

    ...
    
    override fun build(): Mouth = Mouth(...)
}


I think the problem is clear now. Each builder can have one or more dependent ones, and they only needed for declaring dsl functions and scope separation, the code is boilerplate.

So, can there be a simple solution that generalizes the code?

0 Answers
Related