Kotlin cache proxy pattern

How to write a cache proxy pattern in Kotlin

An example of a cache proxy pattern in Kotlin.

Table of contents for design patterns in Kotlin

A cache proxy improves the performance of the underlying object’s members when they perform long-running tasks that return seldom-changing results. Wikipedia

Example:

PrimeNumberService

interface PrimeNumberService {

    fun primeBefore(offset: Int, limit: Int): IntArray
}

CalculatePrimeNumberService

class CalculatePrimeNumberService : PrimeNumberService {

    override fun primeBefore(offset: Int, limit: Int): IntArray = (0..limit).filter { it.isPrime() }.toIntArray()

    private fun Int.isPrime(): Boolean {
        for (i in 2..(this / 2)) {
            Thread.sleep(10) // simulates a long time operation
            if (this % i == 0) {
                return false
            }
        }
        return true
    }
}

CachePrimeNumberService

class CachePrimeNumberService(
    private var primeNumberService: PrimeNumberService
) : PrimeNumberService {

    private val cache: MutableMap<Pair<Int, Int>, IntArray> = mutableMapOf()

    override fun primeBefore(offset: Int, limit: Int): IntArray {
        val key = Pair(offset, limit)
        return cache[key] ?: primeNumberService.primeBefore(offset, limit).apply {
            cache[key] = this
        }
    }
}

Usage:

fun main() {
    val calculatePrimeNumberService = CalculatePrimeNumberService()
    val cachePrimeNumberService = CachePrimeNumberService(calculatePrimeNumberService)

    measureTimeMillis {
        println(calculatePrimeNumberService.primeBefore(1, 100).joinToString())
    }.also { time ->
        println("calculatePrimeNumberService first call: $time millis")
    }

    measureTimeMillis {
        println(calculatePrimeNumberService.primeBefore(1, 100).joinToString())
    }.also { time ->
        println("calculatePrimeNumberService second call: $time millis")
    }

    measureTimeMillis {
        println(cachePrimeNumberService.primeBefore(1, 100).joinToString())
    }.also { time ->
        println("cachedPrimeNumberService first call: $time millis")
    }

    measureTimeMillis {
        println(cachePrimeNumberService.primeBefore(1, 100).joinToString())
    }.also { time ->
        println("cachedPrimeNumberService second call: $time millis")
    }
}

Result

0, 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97
calculatePrimeNumberService first call: 7019 millis
0, 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97
calculatePrimeNumberService second call: 7064 millis
0, 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97
cachedPrimeNumberService first call: 7108 millis
0, 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97
cachedPrimeNumberService second call: 0 millis