代理

什么是代理?

代理就是你想去找老婆,但是你现在没有找老婆的功能(比如不认识女生,没有女生的联系方式),而媒婆有这个功能,那媒婆就是一个代理对象。当你要找老婆时,无需自己去实现找老婆的功能,直接调用媒婆的功能即可。

代理设计模式已经被广泛的应用在各个语言的程序当中,比如Java的Spring技术栈,Android的Retrofit网络框架。代理模式可以将调用主体和代理对象的职责分离,有助于项目的维护。

Kotlin中提供了by关键字,直接从语言层面支持的代理模式,无需我们额外编写任何代码。Kotlin的代理分两种:类代理和属性代理。

类代理

类代理也可以看做另一种实现继承的方式,因为它可以让一个类拥有另外一个类的功能。

先来定义一个接口,接口代表着一种能力:

//码农的功能
interface Coder {
    fun writeCode()
}

现在有个类想拥有Coder的能力:

class Student : Coder

而目前已经有别的类实现了Coder能力:

class A : Coder {
    override fun writeCode() {
        println("write code very happy!")
    }
}

此时,Student类就没必要自己再实现一遍,可以将A的对象作为自己的代理对象,让代理对象帮助我们实现。使用by关键字就可以做到:

class Student(c: Coder) : Coder by c
//调用方法,实际上调用了代理的方法
Student(A()).writeCode() //write code very happy!

当然如果你愿意,也可以选择覆盖代理对象的某个方法实现:

class Student(c: Coder) : Coder by c {
    override fun writeCode() {
        println("write code 996!")
    }
}
Student(A()).writeCode() //write code 996!

但是如果代理对象的方法引用了它自己的属性,我们在自己类中覆盖这个属性则是不会生效的:

interface Coder {
    val company: String
    fun writeCode()
}
class A : Coder {
    override val company = "华为"
    override fun writeCode() {
        println("write code at $company!")
    }
}
class Student(c: Coder) : Coder by c {
    override val company = "阿里巴巴"
}
Student(A()).writeCode() //write code at 华为!

其根本原因是最终调用的是代理对象的方法,并不是自己的方法,因此使用的变量仍然是代理对象自己的。

属性代理

属性代理可以让我们使用另外一个类对象来代理属性的Setter和Getter。

来看一个User类,它有一个name属性:

class User {
    var name: String 
}

假设我们并不想去关心name属性的Getter逻辑和Setter逻辑(比如范围检查之类的逻辑),而是希望让别的代理类来做,此时就可以编写一个属性代理类。

属性代理类不需要实现任何接口,只需要提供getValue()setValue()方法即可,分别对应属性的Getter和Setter。比如:

class NameDelegate {
    private var _value = "defaultValue"
    //当访问属性的getter时调用
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        println("get -> $thisRef '${property.name}' ")
        return _value
    }
	//当访问属性的setter时调用
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        //如果不为空就设置
        if(value.isNotEmpty()) _value = value
        println("set -> $value want set to '${property.name}' in $thisRef.")
    }
}

NameDelegate的作用非常简单,只有传入的值不是空字符串才进行赋值,否则取值时就返回默认值,默认值目前是写死的,可以通过构造参数传入。

接下来使用这个属性代理,并对User对象的属性进行访问:

class User {
    var name: String by NameDelegate()
}
var user = User()
user.name = "123" //输出:set -> 123 want set to 'name' in User@3af49f1c.
user.name //输出:get -> User@3af49f1c 'name' 

上面就是一个属性代理的基本使用,看起来好像跟直接重写属性的Setter和Getter并没有太大区别。那属性代理有什么好处呢?

答案是属性代理将对属性的访问逻辑抽成一个独立的类,便于复用。假设项目中有10个类的某个属性访问逻辑需要自定义时,用Setter和Getter需要在每个类中写一遍,而属性代理只需要写一次即可。

内置代理

标准库已经封装了几种代理,说说其中2个比较常用的:lazy代理Observable代理

lazy代理专门用于属性的延时初始化场景,比如有个集合不想一开始就初始化,等到我们第一次使用它时再进行初始化,好处是可以节省初始内存。lazy只能用在val变量上面,它接收一个函数,将函数的返回值作为属性的值。来看看如何使用:

class User {
    val age: Int by lazy {
        println("do something")
        10
    }
}
var user = User()
println(user.age) //只会打印一次 do something
println(user.age)

值的延迟计算默认是线程安全的,如果你确定你是在单线程场景,可以给lazy传入一个参数来取消这个安全,获得一些性能上的提升:

class User {
    val age: Int by lazy(mode = LazyThreadSafetyMode.NONE) {
        println("do something")
        10
    }
}

Observable代理一般用在我们想在属性值更改时执行一些逻辑的场景,它接收一个属性初始值和属性更改时的处理函数,每次属性被赋值时都会执行这个函数。

来看看Observable代理的用法:

class User {
    var age: Int by Delegates.observable(10){
        property, oldValue, newValue ->
        println("${property.name}的值从${oldValue}修改为$newValue")
    }
}
var user = User()
user.age = 11 //age的值从10修改为11
user.age = 15 //age的值从11修改为15

在Android开发中,lazy代理用的会比较多。其实属性代理功能非常强大,可以用来实现MVVM架构,需要实现一个VM层将类的属性和UI映射起来,监听数据的属性变化,当值被更改时去更新对应UI。

Android官方为了方便大家开发,提供了Jetpack类库,其中的LiveData框架是用Java实现的一个MVVM框架,如果用Kotlin代理来做会更简单一些。

更新时间: 4/30/2019, 9:52:46 PM