什么,属性也能有观察者模式?

本文介绍了Kotlin中利用代理属性实现观察者模式的方法。通过Delegates.observable和Delegates.vetoable函数,可以在属性值改变时触发回调或者有条件地阻止属性赋值。

在前面的博文中,我们一起探究了类代理代理属性,一起深入了解了它们的实现机制,顺便还讨论了下标准库的玩儿法: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时,会停止方法的执行,也就不会赋值了。

这波分析下来,是不是如醍醐灌顶!普通的代理属性加上一些封装却有了类似于观察者模式响应变化的能力。我们也是知道如何实现一个代理属性的,却不一定能做出这样的操作,所以大家还是有时间多读读标准库的源码,总有一些独特的编码示范。

转载于:https://juejin.im/post/5caa0bc9f265da24f65ef81b

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值