不依赖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
相关产品推荐
相关产品推荐

