在代理模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。代理模式是一项基本技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上是在特殊的场合采用了代理模式。
代理模式使得我们可以用聚合来替代继承,它还使我们可以模拟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 后面的属性就是代理,这样这个属性的 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()
}
如果这个属性在第一次写之前读,它就会抛出一个异常,只有分配之后才会正常。
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
这几篇一些概念性的东西需要直观了解的我直接复制了过来,当然也可以看文档,到这类部分就告一段落了。有遗漏的地方随时补充