Kotlin学习笔记:代理

196 阅读3分钟

代理

kotlin委托模式是软件设计模式中的一项基本技巧,又名代理模式。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。 Kotlin 直接支持委托模式,更加优雅,简洁。Kotlin 通过关键字 by 实现委托。by后面就是你委托的对象,可以是一个表达式。因此在本例中,通过by player 委托给了具体的被委托对象。

代理模式使用场景

  • 可以在对目标对象访问前后增加额外的业务逻辑,实现功能增强。例如,给方法增加日志打点,就可以使用代码,而不需要去修改原本已经封装好的代码

代理模式实现

代理模式具体实现思路

  • 创建抽象对象
  • 创建真实对象
  • 创建代理对象

简单Demo

以玩游戏为例

// 被实现类,玩游戏,创建抽象对象
interface IGamePlayer {
    // 打排位赛
    fun rank()
    // 升级
    fun upgrade()
}

正常情况下,玩家自己玩游戏,由GamePlayer实现玩游戏的功能

// 游戏玩家,创建真实对象
class GamePlayer(val name: String) : IGamePlayer{
    override fun rank() {
        println("$name 开始排位赛1")
    }

    override fun upgrade() {
        println("$name 升级了1")
    }

}

玩家玩游戏

fun main() {
    val player1 = GamePlayer("张三")
    player1.rank()
    player1.upgrade()
}

我们也可以将其委托给游戏代练,由代练玩游戏

// 本场景中的游戏代练,创建真实对象
class RealGamePlayer(private val name: String): IGamePlayer{
    override fun rank() {
        println("$name 开始排位赛2")
    }

    override fun upgrade() {
        println("$name 升级了2")
    }

}

// 创建代理对象
class DelegateGamePlayer(private val player: IGamePlayer): IGamePlayer by player

代练玩游戏

fun main() {
    val player1 = RealGamePlayer("李四")
    val delegateGamePlayer = DelegateGamePlayer(player1)
    delegateGamePlayer.rank()
    delegateGamePlayer.upgrade()
}

属性委托

kotlin会对属性默认实现get/set方法,对属性的委托,本质上就是对get/set方法的代理,因此,被委托类需要实现getValue/setValue方法,如果是val 属性,只需提供getValue。如果是var 属性,则setValue/getValue都需要提供。

简单Demo

Kotlin 标准库中声明了2个含所需 operator方法的 ReadOnlyProperty / ReadWriteProperty 接口。分别对应val属性与var属性,以var属性为例

class DelegateValue : ReadWriteProperty<Any,Int>{

    override fun getValue(thisRef: Any, property: KProperty<*>): Int {
        return 20
    }

    override fun setValue(thisRef: Any, property: KProperty<*>, value: Int) {
    }

    operator fun getValue(nothing: Nothing?, property: KProperty<*>): Int {
        return 20
    }

    operator fun setValue(nothing: Nothing?, property: KProperty<*>, i: Int) {

    }
}

var testInt : Int by DelegateValue()

其中的参数解释如下:

  • thisRef —— 必须与 属性所有者 类型(对于扩展属性——指被扩展的类型)相同或者是它的超类型;
  • property —— 必须是类型 KProperty<*>或其超类型。
  • value —— 必须与属性同类型或者是它的子类型。

优劣

这种方式类似于Java中的静态代理,拥有者和静态代理同样的优缺点
优点:

  • 实现简单
  • 用户只需要使用代理类的方法,不需要关心真正实现方法。实现与真正实现方法的解耦 缺点:
  • 代码复杂,每增加一个接口,都需要新增加一个接口实现类

动态代理

简单Demo

与Java中实现方式一致,使用Proxy.newProxyInstance

class playGameHadler : InvocationHandler{
    private var mTarget: Any? = null

    constructor(target: Any?) {
        this.mTarget = target
    }

    override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
        println("方法执行前")
        // 因为传来的参数 args 是不确定的,所以用 *args.orEmpty() 传参
        val result = method!!.invoke(mTarget, *args.orEmpty())
        println("方法执行后")
        return result
    }
}

接口中的 invoke 方法比较重要。方法体中的 Any 类型的 proxy 是最终生成的代理对象;Method 类型的 method 是被代理的方法;Any数组类型的 args 是被代理方法执行所需要的参数。target 对象就是传入的代理类;result就是执行方法的返回值,如果没有返回值,就返回为null

internal inline fun <reified T : Any> noOpDelegate(player : IGamePlayer): T {
    val javaClass = T::class.java
    return Proxy.newProxyInstance(
            javaClass.classLoader, arrayOf(javaClass), playGameHadler(player)
    ) as T
}

val test = object : IGamePlayer by noOpDelegate(GamePlayer("马六")){
    override fun rank() {
        println("rank累了")
    }
}
test.rank()
test.upgrade()

优劣

相比与静态代理而言:

优势:

  • 代码量少,不需要对每一个抽象类创建一个实现方法

劣势:

  • 实现复杂