DEV Community

Victor Harlan D. Lacson
Victor Harlan D. Lacson

Posted on

Kotlin Design Pattern: Builder

Hello Everyone,
I am starting to lean towards the declarative programming paradigm in Kotlin, also applying design patterns.

What is Imperative Programming?
You give the computer a set of instructions to follow and the computer does what you want in an easy-to-follow sequence.

What is Declarative Programming?
As a process of constantly defining what things are. This is referred to as declarative programming.

Some of the examples I found online it seems to me is in the form of imperative style. So I tried to construct a builder design pattern in declarative way with immutability in mind.

for the code example below

I declared an interface DailySetup with an implementation of data class DailySetuImpl as follows.

Then create a function dailySetup that returns an instance of DailySetup and accepts a higher order function that can access all properties of DailySetupImpl


approach 1

fun main() {
    val setup = dailySetup {
        constant = 1
        timeFunction = 1
        timeMult = 1
    }
    println("calculated setup: "+setup.calculate())
}

fun DailySetup.calculate(): Int {
    return (this.timeFunction*this.timeMult)+this.constant
}

interface  DailySetup {
    val constant: Int
    val timeFunction: Int
    val timeMult: Int
}

data class DailySetupImpl private constructor(
    override var constant: Int = 0,
    override var timeFunction: Int = 0,
    override var timeMult: Int = 0
): DailySetup {
    companion object {
        fun default () = DailySetupImpl()
    }
}

fun dailySetup(block: DailySetupImpl.()-> Unit): DailySetup {
    return DailySetupImpl.default().apply(block)
}
Enter fullscreen mode Exit fullscreen mode

approach 2

fun main() {
    val setup = DailySetupBuilder.create {
        constant = 1
        timeFunction = 1
        timeMult = 1
    }
    println("calculated setup: "+setup.calculate())
}

fun DailySetup.calculate(): Int {
    return (this.timeFunction*this.timeMult)+this.constant
}

interface  DailySetup {
    val constant: Int
    val timeFunction: Int
    val timeMult: Int
}


data class DailySetupImpl private constructor(
    override var constant: Int = 0,
    override var timeFunction: Int = 0,
    override var timeMult: Int = 0
): DailySetup {
    companion object {
        fun default () = DailySetupImpl()
    }
}

class DailySetupBuilder {
    companion object {
        fun create(block: DailySetupImpl.()-> Unit): DailySetup {
            return DailySetupImpl.default().apply(block)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

approach 3
we can utilize sealed classes, sealed classes cannot be explicitly initialized

...
sealed class DailySetupImpl(
    override var constant: Int = 0,
    override var timeFunction: Int = 0,
    override var timeMult: Double = 0.0,
    override var internalObj: SetupInternalObj? = null
): DailySetup {
    object DEFAULT: DailySetupImpl()
}
class DailySetupBuilder {
    companion object {
        fun create(block: DailySetupImpl.()-> Unit): DailySetup {
            return DailySetupImpl.DEFAULT.apply(block)
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

In this way, I think, it is created via declarative way. What do you think of this approach?

Oldest comments (0)