kotlin学习日记(六)类的代理模式

在代理模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。代理模式是一项基本技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上是在特殊的场合采用了代理模式。

代理模式使得我们可以用聚合来替代继承,它还使我们可以模拟mixin(混合类型)。委托模式的作用是将委托者与实际实现代码分离出来,以达成解耦的目的。

像是(三)里面写的延时初始化成员变量的时候所用到的lazy()就是一个懒加载属性代理

类的代理

例子:

package com.easy.kotlin

import java.util.*

/**
 * Created by jack on 2017/7/5.
 */

interface Subject {
    fun hello()
}

class RealSubject(val name: String) : Subject {
    override fun hello() {
        val now = Date()
        println("Hello, REAL $name! Now is $now")
    }
}

class ProxySubject(val sb: Subject) : Subject by sb {
    override fun hello() {
        println("Before ! Now is ${Date()}")
        sb.hello()
        println("After ! Now is ${Date()}")
    }
}

fun main(args: Array) {
    val subject = RealSubject("World")
    subject.hello()
    println("-------------------------")
    val proxySubject = ProxySubject(subject)
    proxySubject.hello()
}

在这个例子中,委托代理类 ProxySubject 继承接口 Subject,并将其所有共有的方法委托给一个指定的对象sb :

class ProxySubject(val sb: Subject) : Subject by sb

ProxySubject 的超类型Subject中的 by sb 表示sb 将会在 ProxySubject 中内部存储。

另外,我们在覆盖重写了函数override fun hello()

测试代码:

fun main(args: Array) {
    val subject = RealSubject("World")
    subject.hello()
    println("-------------------------")
    val proxySubject = ProxySubject(subject)
    proxySubject.hello()
}
输出:
Hello, REAL World! Now is Wed Jul 05 02:45:42 CST 2017
-------------------------
Before ! Now is Wed Jul 05 02:45:42 CST 2017
Hello, REAL World! Now is Wed Jul 05 02:45:42 CST 2017
After ! Now is Wed Jul 05 02:45:42 CST 2017

属性代理

写一个属性代理的例子:

class Delegates{
    val hello by lazy {
        "HelloWorld"
    }

    val hello2 by X()

    var hello3 by X()
}

class X{
    private var value: String? = null

    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        println("getValue: $thisRef -> ${property.name}")
        return value?: ""
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String){
        println("setValue, $thisRef -> ${property.name} = $value")
        this.value = value
    }
}

fun main(args: Array) {
    val delegates = Delegates()
    println(delegates.hello)
    println(delegates.hello2)
    println(delegates.hello3)
    delegates.hello3 = "value of hello3"
    println(delegates.hello3)
}

上述例子就把hello2和hello3的初始化交给了X代理

很多常用属性,虽然我们可以在需要的时候手动实现它们,但更好的办法是一次实现多次使用,并放到库。比如:

延迟属性:只在第一次访问是计算它的值 观察属性:监听者从这获取这个属性更新的通知 在 map 中存储的属性,而不是单独存在分开的字段

为了满足这些情形,Kotllin 支持代理属性:

class Example {
	var p: String by Delegate()
}

语法结构是: val/var : by  在 by 后面的属性就是代理,这样这个属性的 get() 和 set() 方法就代理给了它。

属性代理不需要任何接口的实现,但必须要提供 get() 方法(如果是变量还需要 set() 方法)。像这样:

class Delegate {
	fun get(thisRef: Any?, prop: PropertyMetadata): String {
		return "$thisRef, thank you for delegating '${prop.name}' to me !"
	}
	
	fun set(thisRef: Any?, prop: PropertyMatada, value: String) {
		println("$value has been assigned to '${prop.name} in $thisRef.'")
	}
}

当我们从 p 也就是 Delegate 的代理,中读东西时,会调用 Delegate 的 get() 函数,因此第一个参数是我们从 p 中读取的,第二个参数是 p 自己的一个描述。比如:

val e = Example()
pintln(e.p)

打印结果: 

Example@33a17727, thank you for delegating ‘p’ to me!

同样当我们分配 p 时 set() 函数就会调动。前俩个参数所以一样的,第三个持有分配的值:

e.p = "NEW"

打印结果: 

NEW has been assigned to ‘p’ in Example@33a17727.

代理属性的要求

这里总结一些代理对象的要求。

只读属性 (val),代理必须提供一个名字叫 get 的方法并接受如下参数:

接收者--必须是相同的,或者是属性拥有者的子类型

元数据--必须是 PropertyMetadata 或这它的子类型

这个函数必须返回同样的类型作为属性。

可变属性 (var),代理必须添加一个叫 set 的函数并接受如下参数:

接受者--与 get() 一样 元数据--与 get() 一样 新值--必须和属性类型一致或是它的字类型

标准代理

kotlin.properties.Delegates 对象是标准库提供的一个工厂方法并提供了很多有用的代理

延迟

Delegate.lazy() 是一个接受 lamdba 并返回一个实现延迟属性的代理:第一次调用 get() 执行 lamdba 并传递 lazy() 并记下结果,随后调用 get() 并简单返回之前记下的值。

import kotlin.properties.Delegates

val lazy: String by Delegates.lazy {
    println("computed!")
    "Hello"
}

fun main(args: Array<String>) {
    println(lazy)
    println(lazy)
}

如果你想要线程安全,使用 blockingLazy(): 它还是按照同样的方式工作,但保证了它的值只会在一个线程中计算,并且所有的线程都获取的同一个值。

观察者

Delegates.observable() 需要俩个参数:一个初始值和一个修改者的 handler 。每次我们分配属性时都会调用handler (在分配前执行)。它有三个参数:一个分配的属性,旧值,新值:

class User {
	var name: String by Delegates.observable("") {
		d.old,new -> println("$old -> $new")
	}
}

fun main(args: Array<String>) {
	val user = User()
	user.name = "first"
	user.name = "second"
}

打印结果

-> first first -> second

如果你想能够截取它的分配并取消它,用 vetoable()代替  observable()

非空

有时我们有一个非空的 var ,但我们在构造函数中没有一个合适的值,比如它必须稍后再分配。问题是你不能持有一个未初始化并且是非抽象的属性:

class Foo {
	var bar: Bat //错误必须初始化
}

我们可以用 null 初始化它,但我们不用每次访问时都检查它。

Delegates.notNull()可以解决这个问题

class Foo {
	var bar: Bar by Delegates.notNull()
}

如果这个属性在第一次写之前读,它就会抛出一个异常,只有分配之后才会正常。

在 Map 中存储属性

Delegates.mapVal() 拥有一个 map 实例并返回一个可以从 map 中读其中属性的代理。在应用中有很多这样的例子,比如解析 JSON 或者做其它的一些 "动态"的事情:

class User(val map: Map<String, Any?>) {
	val name: String by Delegates.mapVal(map)
	val age: Int     by Delegates.mapVal(map)
}

在这个例子中,构造函数持有一个 map :

val user = User(mapOf (
	"name" to "John Doe",
	"age" to 25
))

代理从这个 map 中取指(通过属性的名字):

println(user.name) // Prints "John Doe"
println(user.age)  // Prints 25

var 可以用 mapVar

这几篇一些概念性的东西需要直观了解的我直接复制了过来,当然也可以看文档,到这类部分就告一段落了。有遗漏的地方随时补充

你可能感兴趣的:(Kotlin开发,kotlin代理,kotlin开发)