DEV Community

Victor Harlan D. Lacson
Victor Harlan D. Lacson

Posted on

2 2

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?

Playwright CLI Flags Tutorial

5 Playwright CLI Flags That Will Transform Your Testing Workflow

  • 0:56 --last-failed
  • 2:34 --only-changed
  • 4:27 --repeat-each
  • 5:15 --forbid-only
  • 5:51 --ui --headed --workers 1

Learn how these powerful command-line options can save you time, strengthen your test suite, and streamline your Playwright testing experience. Click on any timestamp above to jump directly to that section in the tutorial!

Watch Full Video 📹ī¸

Top comments (0)

👋 Kindness is contagious

DEV is better (more customized, reading settings like dark mode etc) when you're signed in!

Okay