什麼,屬性也能有觀察者模式?

小小小小小粽子-發表於2019-04-07

在前面的博文中,我們一起探究了類代理代理屬性,一起深入瞭解了它們的實現機制,順便還討論了下標準庫的玩兒法:lazy函式,其實除了lazy這種懶載入的函式,還有用代理屬性實現類似觀察者模式的機制,我們一起來看一下。

標準庫給我們提供的是Delegates.observable,它的使用方法如下:

fun main(args:Array<String>){
     var name by Delegates.observable("王小明") { property, oldValue, newValue ->
  println("$oldValue -> $newValue")
     }
  name="海東"
  name="劍"
  }
複製程式碼

observable函式很簡單,第一個引數我們傳入一個初始值,第二個引數我們傳入一個lambda,可以對舊值新值做一些操作,每次賦值都會呼叫我們的lambda,我們這裡在賦值的時候就會列印出舊值新值,事實上你可以在這裡做你想做的任何操作。

很神奇又很簡單,總讓人好奇是怎麼實現的:

public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):
        ReadWriteProperty<Any?, T> =
    object : ObservableProperty<T>(initialValue) {
        override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
    }
複製程式碼

我們看到這個方法返回了我們熟悉的ReadWriteProperty介面的物件,實際上是個ObservableProperty子類的物件,在這裡重寫了afterChange方法,會轉而呼叫我們傳入的lambda。afterChange方法的呼叫邏輯則放在ObservableProperty類裡面:

public abstract class ObservableProperty<T>(initialValue: T) : ReadWriteProperty<Any?, T> {
    private var value = initialValue

    protected open fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = true

    protected open fun afterChange(property: KProperty<*>, oldValue: T, newValue: T): Unit {}

    public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return value
  }

    public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        val oldValue = this.value
  if (!beforeChange(property, oldValue, value)) {
            return
  }
        this.value = value
        afterChange(property, oldValue, value)
    }
}
複製程式碼

主要邏輯在setValue方法裡面,我們可以看到,在每次賦值完成之後,都會呼叫我們的afterChange方法,注意在此之前我們有一個if判斷,會終止方法的執行,不過由於我們這兒並沒有重寫beforeChange方法,所以該方法一直返回trueafterChange在這裡一定會被呼叫。

beforeChange有什麼用呢,這就要說到我們的另一個方法了。

var maxLength: String by Delegates.vetoable("init value") { property, oldValue, newValue ->
  newValue.length > oldValue.length }
複製程式碼

這裡的vetoble方法只有在我們傳入的lambda返回true的時候才會執行賦值。

我們來看一下實現:

public inline fun <T> vetoable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Boolean):
        ReadWriteProperty<Any?, T> =
    object : ObservableProperty<T>(initialValue) {
        override fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = onChange(property, oldValue, newValue)
    }
複製程式碼

哦喲,這裡還是實現的相同的ObservableProperty,這不過這次重寫的是beforeChange,也就是說,在這種情況下,呼叫setValue的時候,我們有可能會不執行賦值操作。

public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
    val oldValue = this.value
  if (!beforeChange(property, oldValue, value)) {
        return
  }
    this.value = value
    afterChange(property, oldValue, value)
}
複製程式碼

當我們傳入的lambda返回false時,會停止方法的執行,也就不會賦值了。

這波分析下來,是不是如醍醐灌頂!普通的代理屬性加上一些封裝卻有了類似於觀察者模式響應變化的能力。我們也是知道如何實現一個代理屬性的,卻不一定能做出這樣的操作,所以大家還是有時間多讀讀標準庫的原始碼,總有一些獨特的編碼示範。

相關文章