You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

不依赖Spring缓存的Kotlin接口方法缓存惯用实现方案

可行方案:装饰器模式 + 扩展记忆化能力

完全满足你提出的三项要求,无需修改原有实现类、无需改动接口定义、客户端无感知,符合Kotlin函数式编程风格。


步骤1:扩展原有Memoize能力,支持多参数函数记忆化

不用在每个场景重复定义缓存Key,直接为不同参数数量的函数添加记忆化扩展:

// 原有单参数记忆化实现保持不变
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)

// 新增双参数函数记忆化扩展,适配当前multiply方法的参数结构
fun <T1, T2, R> ((T1, T2) -> R).memoize(): (T1, T2) -> R {
    data class CacheKey(val t1: T1, val t2: T2)
    val wrapped = { key: CacheKey -> this(key.t1, key.t2) }.memoize()
    return { t1, t2 -> wrapped(CacheKey(t1, t2)) }
}

步骤2:实现接口缓存装饰类

完全不用修改原有CalculatorDumb、CalculatorSmart的代码,只需要加一个统一的接口装饰类,所有缓存逻辑都收敛在这里:

class CachedCalculate(private val delegate: ICalculate) : ICalculate {
    // 直接将真实实现的方法包装为记忆化函数
    private val cachedMultiply = delegate::multiply.memoize()
    override fun multiply(a: Int, b: Int): Int = cachedMultiply(a, b)
}

步骤3:客户端使用

对外仍然暴露原始ICalculate接口,客户端感知不到缓存逻辑的存在:

fun main() {
    val calSmart: ICalculate = CachedCalculate(CalculatorSmart())
    println(calSmart.multiply(3, 7)) // 首次调用输出cache miss,返回21
    println(calSmart.multiply(3, 7)) // 缓存命中,无miss日志,直接返回21

    val calDumb: ICalculate = CachedCalculate(CalculatorDumb())
    println(calDumb.multiply(3, 7)) // 首次调用输出cache miss,返回21
    println(calDumb.multiply(3, 7)) // 缓存命中,无miss日志,直接返回21
}

可选扩展

  • 如果需要全局共享缓存,把cachedMultiply放到CachedCalculate的伴生对象中即可
  • 如果需要缓存过期、淘汰策略,只需修改Memoize内部的存储实现,替换为支持淘汰的Map实现(如Guava Cache)即可

内容的提问来源于stack exchange,提问作者smallufo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 05:21:00