smallufo
09/17/2021, 5:29 PMinterface ICalculate {
fun multiply(a: Int, b: Int): Int
}
It just multiplies a
and b
, and return the result . Suppose it is a heavy computation work . And there are two implementations :
class CalculatorDumb : ICalculate {
override fun multiply(a: Int, b: Int): Int {
var sum = 0
(1..a).forEach {
(1..b).forEach {
sum++
}
}
return sum
}
}
The dumb implementation just add one by one .
class CalculatorSmart : ICalculate {
override fun multiply(a: Int, b: Int): Int {
return a * b
}
}
And this smart implementation just returns a * b
.
OK , here is the point . I hope client can initialize no matter dumb or smart implementation , and can get result if the parameter is identical.
There is a Memoize
pattern , described here : https://jorgecastillo.dev/kotlin-purity-and-function-memoization :
class Memoize<in T, out R>(val f: (T) -> R) : (T) -> R {
private val values = mutableMapOf<T, R>()
override fun invoke(x: T): R {
return values.getOrPut(x) { f(x) }
}
}
fun <T, R> ((T) -> R).memoize(): (T) -> R = Memoize(this)
I can use it in the implementation class , like this :
class CalculatorSmart : ICalculate {
data class CacheKey(val a: Int, val b: Int)
private val innerCalculate: (CacheKey) -> Int = { key: CacheKey ->
println("cache miss")
key.a * key.b
}.memoize()
override fun multiply(a: Int, b: Int): Int {
return innerCalculate(CacheKey(a, b))
}
}
But it seems it’s hard to apply it in the interface layer.
I wonder if there are any patterns to achieve :
1. Each implementation class ( dumb or smart in this example) doesn’t need to implement its cache .
2. There are no two versions of method ( multiply()
and cachedMultiply()
for example )
3. Client only knows one method of the interface , No matter the client initialize smart or dumb class , the result of the same parameter will be cached and returned.
For example : such scenario is OK
val calSmart: ICalculate = CalculatorSmart()
println(calSmart.multiply(3, 7)) // cache miss
println(calSmart.multiply(3, 7)) // cache hit
val calDumb: ICalculate = CalculatorDumb()
println(calDumb.multiply(3, 7)) // cache miss
println(calDumb.multiply(3, 7)) // cache hit
It will work like Spring’s method cache . but I hope there will be a kotlin-idiomatic style , maybe more functional , just like the memoization pattern above .
Is there any idea ?
Thanks.Jukka Siivonen
09/18/2021, 8:33 AMephemient
09/18/2021, 9:26 PMephemient
09/18/2021, 10:49 PMfib(100) = Failure(java.util.concurrent.TimeoutException)
memoFib(100) = TimedValue(value=354224848179261915075, duration=4.192067ms)
ack(3, 12) = Failure(java.util.concurrent.TimeoutException)
memoAck(3, 12) = TimedValue(value=32765, duration=150.723097ms)