Friday, June 2, 2023

Memento Design Pattern with Kotlin

 Memento Design Patterns

Since Abhi became a manager, it's been very hard to catch him if I have a question. And when I do ask him something, he just throws something and runs to the next meeting. Yesterday, I asked him what the next weapon we would introduce in our Maronic game should be. He told me it should be a Coconut Cannon, clear as day. But today, when I presented him with this feature, he chirped at me angrily! He said he told me to implement a Pineapple Launcher instead. I'm lucky he's just a canary still... But it would be great if I could just record him, and when we have another meeting that goes awry because he's not paying full attention, I would just replay everything he said.

Remembrance 

Summing up my problems first—Abhi's thoughts are his and his only: 

class Manager {

    private var lastThought = "Should get some coffee"

    private var repeatThat = 3

    private var thenHesitate = "Or maybe tea?"

    private var secretThought = "No, coffee it is"

    ...

Moreover, they're quite complex and scattered. I have no access to them, but only to their byproduct: 

class Manager {

    ...

    fun whatAreYouThinking() {

        for (i in 1..repeatThat) {

            println(lastThought)

        }

        println(thenHesitate)

    }

    ...

}

Even recording what he says is quite hard (because he doesn't return anything).

And even if I did record him, Abhi can claim it's what he said, not what he meant: Why did you bring me tea? I wanted coffee! The solution may seem quite obvious. Let's use an inner class, thought, that will capture this last thought:

class Manager {

    ...

    class Thought {

        fun captureThought(): CapturedThought {

            return CapturedThought(lastThought, 

                                   repeatThat,                              

                                   thenHesitate, 

                                   secretThought)

        }

    }

 data class CapturedThought(val thought: String, 

                               val repeat: Int, 

                               val hesitate: String,

                               val secret: String)

The only problem is that this code doesn't compile. It's because we're missing a new keyword, inner, to mark our class. If we omit this keyword, the class is called Nested, and is similar to the static nested class from Java. Now we can record what Abhi says at this moment:

val abhi = Manager()

 val captured = abhi.Thought().captureThought() Let's assume that abhi changes his mind at some point. We'll add another function for that: 

 class Manager {

    ...

    fun anotherThought() {

        lastThought = "Tea would be better"

        repeatThat = 2

        thenHesitate = "But coffee is also nice"

        secretThought = "Big latte would be great"

    }

}

abhi.anotherThought() 

We can always repeat the thought that we captured: 

abhi.whatAreYouThinking() This will print: 

Tea would be better

Tea would be better

But coffee is also nice Let's check what we've captured: 

println(captured)

This will print: CapturedThought(thought=Should get some coffee, repeat=3, hesitate=Or maybe tea?, secret=No, coffee it is) We can even rewind Abhi's thoughts if he would allow it:

 class Manager {

    ...

    inner class Thought {

        ...

        fun rewindThought(val previousThought: CapturedThought) {

            with(previousThought) {

                lastThought = thought

                repeatThat = repeat

                thenHesitate = hesitate

                secretThought = secret

            }

        }

    }

    ...

}

Note how here we use the with standard function to avoid repeating previousThought on each line.

Code Example :

package designpatterns.memento

fun main(args: Array<String>) {
val abhi = Manager()

val captured = abhi.Thought().captureThought()
abhi.anotherThought()
abhi.whatAreYouThinking()
println(captured)
}

class Manager {
private var lastThought = "Should get some coffee"
private var repeatThat = 3
private var thenHesitate = "Or maybe tea?"
private var secretThought = "No, coffee it is"
fun whatAreYouThinking() {
for (i in 1..repeatThat) {
println(lastThought)
}
println(thenHesitate)
}

inner class Thought {
fun captureThought(): CapturedThought {
return CapturedThought(lastThought, repeatThat, thenHesitate, secretThought)
}

fun rewindThought(previousThought: CapturedThought) {
with(previousThought) {
lastThought = thought
repeatThat = repeat
thenHesitate = hesitate
secretThought = secret
}
}
}

data class CapturedThought(
val thought: String, val repeat: Int, val hesitate: String, val secret: String
)

fun anotherThought() {
lastThought = "Tea would be better"
repeatThat = 2
thenHesitate = "But coffee is also nice"
secretThought = "Big latte would be great"
}
}

Output :

Tea would be better Tea would be better But coffee is also nice CapturedThought(thought=Should get some coffee,

repeat=3, hesitate=Or maybe tea?, secret=No, coffee it is)



No comments: