Sunday, June 4, 2023

Template Method Design Pattern with Kotlin and Comparative Analysis with Java

 Template method Design Patterns

Template method Some lazy people make art out of their laziness. Take me for example. Here's my daily schedule: 8:00–9:00: Arrive at the office 9:00–10:00: Drink coffee 10:00–12:00: Attend some meetings or review code 12:00–13:00: Go out for lunch 13:00–16:00: Attend some meetings or review code 16:00: Sneak out home As you can see, some of the parts of the schedule never change, and some do. At first, I thought I could decorate my changing schedule with that setup and teardown logic, which happens before and after. But then there's lunch, which is holy for architects and happens in between. Java is pretty clear on what you should do. First, you create an abstract class. All methods that you want to implement by yourself you mark as private: 

abstract class DayRoutine {

    private fun arriveToWork() {


println("Hi boss! I appear in the office sometimes!")

    }

 private fun drinkCoffee() {

        println("Coffee is delicious today")

    }

 ...

 private fun goToLunch() {

        println("Hamburger and chips, please!")

    }

 ...

 private fun goHome() {

        // Very important no one notices me

        println()

    }

 ...

For all methods that are changing from day to day, you define an abstract: 

abstract class DayRoutine {

    ...

    abstract fun doBeforeLunch()

    ...

    abstract fun doAfterLunch()

    ...

}


If you allow the changing of a method, but want to provide a default implementation, you leave it public: 

abstract class DayRoutine {

    ...

    open fun bossHook() {

        // Hope he doesn't hook me there

    }

    ...

And finally, you have a method that executes your algorithm. It's final by default: 

abstract class DayRoutine {

    ...

    fun runSchedule() {

        arriveToWork()

        drinkCoffee()

        doAfterLunch()

        goToLunch()

        doAfterLunch()

        goHome()

    }

If we now want to have a schedule for Monday, we simply implement the missing parts: 

class MondaySchedule : DayRoutine() {

    override fun doBeforeLunch() {

        println("Some pointless meeting")


println("Code review. What this does?")

    }

 override fun doAfterLunch() {

        println("Meeting with Ralf")

        println("Telling jokes to other architects")

    }

 override fun bossHook() {

        println("Hey, can I have you for a sec in my office?")

    }

What does Kotlin add on top of that? What it usually does—conciseness. As we've seen previously, this can be achieved through functions. We have three moving parts—two mandatory activities (the software architect must do something before and after lunch) and one optional (the boss may stop him before he sneaks off home or not): 

fun runSchedule(beforeLunch: ()->Unit,

                afterLunch: ()->Unit,

                bossHook: (()->Unit)? = fun() { println() }) {

    ...

We'll have a function that accepts up to three other functions as its arguments. The first two are mandatory, and the third may not be supplied at all, or assigned with

null to explicitly state that we don't want that function to happen: fun runSchedule(...) {

    ...

    arriveToWork()

    drinkCoffee()

    beforeLunch()

    goToLunch()

    afterLunch()

    bossHook?.let { it() }

    goHome()

Inside this function, we'll have our algorithm. Invocations of beforeLunch() and afterLunch() should be clear; after all, those are the functions that are passed to us as arguments. The third one, bossHook, may be null, so we execute it only if it's not: ?.let { it() }. But what about the other functions, those we want to always implement by ourselves? Kotlin has a notion of local functions. Those are functions that reside in other functions: 

fun runSchedule(...) {

    fun arriveToWork(){

        println("How are you all?")

    }

val drinkCoffee = { println("Did someone left the milk out?") }

 fun goToLunch() = println("I would like something italian")

 val goHome = fun () {

        println("Finally some rest")

    }

 arriveToWork()

    drinkCoffee()

    ...

    goToLunch()

    ... goHome()

Those are all valid ways to declare a local function. No matter how you define them, they're invoked in the same way. We're left with the same result, as you can see. Define the algorithm structure, but let others decide what to do at some points: that's what the Template Method is all about.

Code Example :

With Kotlin Example :

package designpatterns.templateMethod


fun main(args: Array<String>) {
runSchedule(afterLunch = fun() {
println("Discuss my lunch with boss' secretary")
println("Read something not related to work")
}, beforeLunch = {
println("Look for my next trip destination")
println("Read StackOverflow")
}, bossHook = { println("Boss: Can we talk privately?") })
}


fun runSchedule(
beforeLunch: () -> Unit,
afterLunch: () -> Unit,
bossHook: (() -> Unit)? = fun() { println() }
) {
fun arriveToWork() {
println("How are you all?")
}

val drinkCoffee = { println("Did someone left the milk out?") }

fun goToLunch() = println("I would like something italian")

val goHome = fun() {
println("Finally some rest")
}

arriveToWork()
drinkCoffee()
beforeLunch()
goToLunch()
afterLunch()
bossHook?.let { it() }
goHome()
}

Output :
How are you all? Did someone left the milk out? Look for my next trip destination Read StackOverflow I would like something italian Discuss my lunch with boss' secretary Read something not related to work Boss: Can we talk privately? Finally some rest

With Java Example :

package chapter4.templateMethod

abstract class DayRoutine {
private fun arriveToWork() {
println("Hi boss! I appear in the office sometimes!")
}

private fun drinkCoffee() {
println("Coffee is delicious today")
}

abstract fun doBeforeLunch()

private fun goToLunch() {
println("Hamburger and chips, please!")
}

abstract fun doAfterLunch()

open fun bossHook() {
// Hope he doesn't hook me there
}

private fun goHome() {
// Very important no one notices me
println()
}

fun runSchedule() {
arriveToWork()
drinkCoffee()
doAfterLunch()
goToLunch()
doAfterLunch()
bossHook()
goHome()
}
}

class MondaySchedule : DayRoutine() {
override fun doBeforeLunch() {
println("Some pointless meeting")
println("Code review. What this does?")
}

override fun doAfterLunch() {
println("Meeting with Ralf")
println("Telling jokes to other architects")
}

override fun bossHook() {
println("Hey, can I have you for a sec in my office?")
}
}

I hope you are able to understand the difference with code examples.


No comments: