一、什么是 Kotlin 特点托付
提到特点托付之前,咱们先来认识一下托付形式(delegation pattern),引证 WIKI 上的解释:
托付形式(delegation pattern)是软件设计形式中的一项根本技巧。在托付形式中,有两个对象参与处理同一个恳求,接受恳求的对象将恳求托付给另一个对象来处理。托付形式是一项根本技巧,许多其他的形式,如状态形式、策略形式、拜访者形式本质上是在更特别的场合采用了托付形式。托付形式使得咱们能够用聚合来替代继承,它还使咱们能够模拟mixin。
浅显来讲,托付就是将自己要做的事情,托付给其他人来做。
Kotlin 的特点托付也是如此,将特点的 获取(get()
) 与 赋值(set()
) 托付给别的类来做。
详细语法如下:
val/var <特点名>: <类型> by <表达式>
在by
后面的表达式是该托付, 因为特点对应的get()
(与set()
)会被托付给它的getValue()
与setValue()
办法。 特点的托付不用完结接口,可是需要供给一个getValue()
函数(关于var
特点还有setValue()
)
举个:
class Delegate {
private var propertyDelegate: Int = 0
private val offset = 100
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
return propertyDelegate
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
propertyDelegate = (value + offset)
}
}
class DelegateTest {
private var testProperty by Delegate()
@Test
fun test() {
println(testProperty) // 0
testProperty = 100
println(testProperty) // 200
testProperty = -100
println(testProperty) // 0
}
}
在上面的比如中咱们将特点 testProperty
托付给 Delegate
完结,我为了示例效果在 Delegate
中对特点增加了一个偏移量,每次赋值都会额外加上这个偏移量,实际测试的打印效果也符合预期。
从这个中咱们其实能够得到一些启示:
咱们能够干涉特点的获取和赋值这两个行为,能够定制咱们想要的一些行为。
关于托付特点的介绍先到此为止,想了解关于托付特点更多的内容能够拜访 Kotlin 的文档 特点托付
二、运用 Kotlin 特点托付封装 MMKV
运用预览
咱们先来看一下 MMKV 运用特点托付封装后怎么运用
var clickCount by MMKVDelegate("click_count", 0)
var timestamp by MMKVDelegate("timestamp", 0L)
var message by MMKVDelegate("message", "Hello World!")
fun test() {
clickCount += 1
timestamp = System.currentTimeMillis()
Log.d("qqq", "clickCount:$clickCount timestamp:$timestamp message:$message")
clickCount = 0
timestamp = 0L
message = "clean"
Log.d("qqq", "clickCount:$clickCount timestamp:$timestamp message:$message")
}
// 输出成果如下:
// clickCount:1 timestamp:1688505455748 message:Hello World!
// clickCount:0 timestamp:0 message:clean
这样运用是不是简单了许多呢?就和操作普通特点千篇一律,简直不要太简单。
咱们废话不多说直接上干货:
封装思路
-
MMKV 的数据获取及写入,需要调用其
encode()
/decodeXxx()
办法来进行,这其实就和特点的get()
/set()
办法千篇一律,所以咱们能够将数据的获取及写入经过特点托付来完结。 -
MMKV 支撑的类型很多,重载办法也许多,所以咱们还需要对数据获取和写入的模版代码进行一次封装处理,我这边只示例根本类型和 String。
1.首要对数据获取及写入的模版代码封装,一致操作入口
import com.tencent.mmkv.MMKV
object MMKVUtils {
private val mmkv = MMKV.defaultMMKV()
fun <T> encode(key: String, value: T) {
when (value) {
is Int -> mmkv.encode(key, value)
is Long -> mmkv.encode(key, value)
is Float -> mmkv.encode(key, value)
is Double -> mmkv.encode(key, value)
is Boolean -> mmkv.encode(key, value)
is String -> mmkv.encode(key, value)
}
}
@Suppress("UNCHECKED_CAST")
fun <T> decode(key: String, defValue: T): T {
return when (defValue) {
is Int -> mmkv.decodeInt(key, defValue) as T
is Long -> mmkv.decodeLong(key, defValue) as T
is Float -> mmkv.decodeFloat(key, defValue) as T
is Double -> mmkv.decodeDouble(key, defValue) as T
is Boolean -> mmkv.decodeBool(key, defValue) as T
is String -> mmkv.decodeString(key, defValue) as T
else -> defValue
}
}
}
上述代码,咱们经过常用手法(泛型)来将数据获取及写入的模版代码一致到两个办法中,其实本来 MMKV.encode()
办法的重载就能够自动识别参数类型,可是 MMKV.decodeXxx()
必须要指定详细类型的办法。
关于 MMKV 的实例能够根据咱们自身项目的需求来进行构建,我这边为了便利直接运用了 defaultMMKV()
办法来构建了默认实例。
2.运用特点托付来封装运用办法
咱们先来认识下 Kotlin 供给的两个类:
- kotlin.properties.ReadOnlyProperty
- kotlin.properties.ReadWriteProperty
这两个类能够帮助咱们完结只读/可读可写特点的托付,能够减少咱们重复写一些模板代码,这两个类的源码如下:
/**
* Base interface that can be used for implementing property delegates of read-only properties.
*
* This is provided only for convenience; you don't have to extend this interface
* as long as your property delegate has methods with the same signatures.
*
* @param T the type of object which owns the delegated property.
* @param V the type of the property value.
*/
public fun interface ReadOnlyProperty<in T, out V> {
/**
* Returns the value of the property for the given object.
* @param thisRef the object for which the value is requested.
* @param property the metadata for the property.
* @return the property value.
*/
public operator fun getValue(thisRef: T, property: KProperty<*>): V
}
/**
* Base interface that can be used for implementing property delegates of read-write properties.
*
* This is provided only for convenience; you don't have to extend this interface
* as long as your property delegate has methods with the same signatures.
*
* @param T the type of object which owns the delegated property.
* @param V the type of the property value.
*/
public interface ReadWriteProperty<in T, V> : ReadOnlyProperty<T, V> {
/**
* Returns the value of the property for the given object.
* @param thisRef the object for which the value is requested.
* @param property the metadata for the property.
* @return the property value.
*/
public override operator fun getValue(thisRef: T, property: KProperty<*>): V
/**
* Sets the value of the property for the given object.
* @param thisRef the object for which the value is requested.
* @param property the metadata for the property.
* @param value the value to set.
*/
public operator fun setValue(thisRef: T, property: KProperty<*>, value: V)
}
因为咱们的数据一般都是可读可写的,所以咱们借助 ReadWriteProperty
类来完结咱们的托付逻辑:
import kotlin.properties.ReadWriteProperty
class MMKVDelegate<T>(private val key: String, private val def: T) : ReadWriteProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T = MMKVUtils.decode(key, def)
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) =
MMKVUtils.encode(key, value)
}
其实上面的代码也很简单,将特点的拜访(get()
)重写为经过 MMKVUtils.decode()
办法来获取 MMKV 中指定 Key 的数据,将特点的赋值(set()
)重写为经过 MMKVUtils.encode()
办法来给指定 Key 设置值。
其中咱们在主构造中,需要传入 Key 和 默认值,这样咱们就能够像操作正常特点相同来对 MMKV 中指定 Key 进行获取/赋值,同时还将 MMKV 的操作进行阻隔,便利后期进行替换,如替换为 DataStore,咱们就能够直接修正 MMKVDelegate
完结替换,试想一下,假如没有对 MMKV 的代码进行阻隔,替换工作将有多么的庞大。