/var/log/jsoizo

メモ帳 技術とか趣味とか

KotlinでMinimal Cake Pattern

出来そうだなと思ったのでお試し。

Minimal Cake PatternといえばScalaにおけるDIの実装パターンの1つで、コンパイル時に依存性を解決でき、DIコンテナ使ってるときにやりがちなDI用アノテーションのつけ忘れて実行時エラーが起きるみたいなことが無いのが特徴。
かつコンパイルが通ればDIできているという安心感のプログラミング体験も最高。
あとは言語機能だけでDIできるので、AWS Lambdaみたいなフットプリントを小さくしたい環境とかで役立つかもしれない。

Kotlinでもだいたい似たようなことができるぞ

import java.time.Clock
import java.time.Instant
import java.time.ZoneId

interface UseClock {
    val clock: Clock
}

interface ISystemClock: UseClock {
    override val clock: Clock get() = Clock.systemUTC()
}

interface IFixedClock: UseClock {
    override val clock: Clock get() = Clock.fixed(Instant.parse("2023-01-20T12:34:56.999999Z"), ZoneId.of("UTC"))
}



abstract class StopWatch(): UseClock {
    private var started: Instant? = null
    fun start() {
        started = clock.instant()
    }
    fun stop() {
        started?.let { started ->
            val elapsed = clock.instant().toEpochMilli() - started.toEpochMilli()
            println("elapsed: $elapsed")
        } ?: throw IllegalStateException("not started")
    }
}

fun main() {
    val systemClockStopWatch = object : StopWatch(), ISystemClock {}
    systemClockStopWatch.start()
    Thread.sleep(1000)
    systemClockStopWatch.stop()

    val fixedClockStopWatch = object : StopWatch(), IFixedClock {}
    fixedClockStopWatch.start()
    Thread.sleep(1000)
    fixedClockStopWatch.stop()
}

元のScala版のMinimal Cake Patternだと MixinSystemClock となっているがKotlinにmixinの機構はないのでので ISystemClock としている。 また、Scala版では MixinSystemClockUseClock を継承する必要はないが、このKotlinの実装においては ISystemClockUseClock を継承しないと object: Stopwatch(): ISystemClock {}コンパイルが通らない点に留意する。

参考

qiita.com