7.13 委托(Delegation)

7.13.1 代理模式(Proxy Pattern)

代理模式,也称委托模式。

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

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

一个代理模式的Java代码示例:

  1. package com.easy.kotlin;
  2. interface JSubject {
  3. public void request();
  4. }
  5. class JRealSubject implements JSubject {
  6. @Override
  7. public void request() {
  8. System.out.println("JRealSubject Requesting");
  9. }
  10. }
  11. class JProxy implements JSubject {
  12. private JSubject subject = null;
  13. //通过构造函数传递代理者
  14. public JProxy(JSubject sub) {
  15. this.subject = sub;
  16. }
  17. @Override
  18. public void request() { //实现接口中定义的方法
  19. this.before();
  20. this.subject.request();
  21. this.after();
  22. }
  23. private void before() {
  24. System.out.println("JProxy Before Requesting ");
  25. }
  26. private void after() {
  27. System.out.println("JProxy After Requesting ");
  28. }
  29. }
  30. public class DelegateDemo {
  31. public static void main(String[] args) {
  32. JRealSubject jRealSubject = new JRealSubject();
  33. JProxy jProxy = new JProxy(jRealSubject);
  34. jProxy.request();
  35. }
  36. }

输出:

  1. JProxy Before Requesting
  2. JRealSubject Requesting
  3. JProxy After Requesting
  4. ```### 7.13.2 类的委托(Class Delegation)
  5. 就像支持单例模式的object对象一样,Kotlin 在语言层面原生支持委托模式。
  6. 代码示例:
  7. ```kotlin
  8. package com.easy.kotlin
  9. import java.util.*
  10. interface Subject {
  11. fun hello()
  12. }
  13. class RealSubject(val name: String) : Subject {
  14. override fun hello() {
  15. val now = Date()
  16. println("Hello, REAL $name! Now is $now")
  17. }
  18. }
  19. class ProxySubject(val sb: Subject) : Subject by sb {
  20. override fun hello() {
  21. println("Before ! Now is ${Date()}")
  22. sb.hello()
  23. println("After ! Now is ${Date()}")
  24. }
  25. }
  26. fun main(args: Array<String>) {
  27. val subject = RealSubject("World")
  28. subject.hello()
  29. println("-------------------------")
  30. val proxySubject = ProxySubject(subject)
  31. proxySubject.hello()
  32. }

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

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

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

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

测试代码:

  1. fun main(args: Array<String>) {
  2. val subject = RealSubject("World")
  3. subject.hello()
  4. println("-------------------------")
  5. val proxySubject = ProxySubject(subject)
  6. proxySubject.hello()
  7. }

输出:

  1. Hello, REAL World! Now is Wed Jul 05 02:45:42 CST 2017
  2. -------------------------
  3. Before ! Now is Wed Jul 05 02:45:42 CST 2017
  4. Hello, REAL World! Now is Wed Jul 05 02:45:42 CST 2017
  5. After ! Now is Wed Jul 05 02:45:42 CST 2017

7.13.3 委托属性 (Delegated Properties)

通常对于属性类型,我们是在每次需要的时候手动声明它们:

  1. class NormalPropertiesDemo {
  2. var content: String = "NormalProperties init content"
  3. }

那么这个content属性将会很“呆板”。属性委托赋予了属性富有变化的活力。

例如:

  • 延迟属性(lazy properties): 其值只在首次访问时计算
  • 可观察属性(observable properties): 监听器会收到有关此属性变更的通知
  • 把多个属性储存在一个映射(map)中,而不是每个存在单独的字段中。

    委托属性

Kotlin 支持 委托属性:

  1. class DelegatePropertiesDemo {
  2. var content: String by Content()
  3. override fun toString(): String {
  4. return "DelegatePropertiesDemo Class"
  5. }
  6. }
  7. class Content {
  8. operator fun getValue(delegatePropertiesDemo: DelegatePropertiesDemo, property: KProperty<*>): String {
  9. return "${delegatePropertiesDemo} property '${property.name}' = 'Balalala ... ' "
  10. }
  11. operator fun setValue(delegatePropertiesDemo: DelegatePropertiesDemo, property: KProperty<*>, value: String) {
  12. println("${delegatePropertiesDemo} property '${property.name}' is setting value: '$value'")
  13. }
  14. }

var content: String by Content()中, by 后面的表达式的Content()就是该属性委托的对象。content属性对应的 get()(和 set())会被委托给Content()operator fun getValue()operator fun setValue() 函数,这两个函数是必须的,而且得是操作符函数。

测试代码:

  1. val n = NormalPropertiesDemo()
  2. println(n.content)
  3. n.content = "Lao tze"
  4. println(n.content)
  5. val e = DelegatePropertiesDemo()
  6. println(e.content) // call Content.getValue
  7. e.content = "Confucius" // call Content.setValue
  8. println(e.content) // call Content.getValue

输出:

  1. NormalProperties init content
  2. Lao tze
  3. DelegatePropertiesDemo Class property 'content' = 'Balalala ... '
  4. DelegatePropertiesDemo Class property 'content' is setting value: 'Confucius'
  5. DelegatePropertiesDemo Class property 'content' = 'Balalala ...

懒加载属性委托 lazy

lazy() 函数定义如下:

  1. @kotlin.jvm.JvmVersion
  2. public fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

它接受一个 lambda 并返回一个 Lazy <T> 实例的函数,返回的实例可以作为实现懒加载属性的委托:

第一次调用 get() 会执行已传递给 lazy() 的 lamda 表达式并记录下结果, 后续调用 get() 只是返回之前记录的结果。

代码示例:

  1. val synchronizedLazyImpl = lazy({
  2. println("lazyValueSynchronized1 3!")
  3. println("lazyValueSynchronized1 2!")
  4. println("lazyValueSynchronized1 1!")
  5. "Hello, lazyValueSynchronized1 ! "
  6. })
  7. val lazyValueSynchronized1: String by synchronizedLazyImpl
  8. println(lazyValueSynchronized1)
  9. println(lazyValueSynchronized1)
  10. val lazyValueSynchronized2: String by lazy {
  11. println("lazyValueSynchronized2 3!")
  12. println("lazyValueSynchronized2 2!")
  13. println("lazyValueSynchronized2 1!")
  14. "Hello, lazyValueSynchronized2 ! "
  15. }
  16. println(lazyValueSynchronized2)
  17. println(lazyValueSynchronized2)

输出:

  1. lazyValueSynchronized1 3!
  2. lazyValueSynchronized1 2!
  3. lazyValueSynchronized1 1!
  4. Hello, lazyValueSynchronized1 !
  5. Hello, lazyValueSynchronized1 !
  6. lazyValueSynchronized2 3!
  7. lazyValueSynchronized2 2!
  8. lazyValueSynchronized2 1!
  9. Hello, lazyValueSynchronized2 !
  10. Hello, lazyValueSynchronized2 !

默认情况下,对于 lazy 属性的求值是同步的(synchronized), 下面两种写法是等价的:

  1. val synchronizedLazyImpl = lazy({
  2. println("lazyValueSynchronized1 3!")
  3. println("lazyValueSynchronized1 2!")
  4. println("lazyValueSynchronized1 1!")
  5. "Hello, lazyValueSynchronized1 ! "
  6. })
  7. val synchronizedLazyImpl2 = lazy(LazyThreadSafetyMode.SYNCHRONIZED, {
  8. println("lazyValueSynchronized1 3!")
  9. println("lazyValueSynchronized1 2!")
  10. println("lazyValueSynchronized1 1!")
  11. "Hello, lazyValueSynchronized1 ! "
  12. })

该值是线程安全的。所有线程会看到相同的值。

如果初始化委托多个线程可以同时执行,不需要同步锁,使用LazyThreadSafetyMode.PUBLICATION

  1. val lazyValuePublication: String by lazy(LazyThreadSafetyMode.PUBLICATION, {
  2. println("lazyValuePublication 3!")
  3. println("lazyValuePublication 2!")
  4. println("lazyValuePublication 1!")
  5. "Hello, lazyValuePublication ! "
  6. })

而如果属性的初始化是单线程的,那么我们使用 LazyThreadSafetyMode.NONE 模式(性能最高):

  1. val lazyValueNone: String by lazy(LazyThreadSafetyMode.NONE, {
  2. println("lazyValueNone 3!")
  3. println("lazyValueNone 2!")
  4. println("lazyValueNone 1!")
  5. "Hello, lazyValueNone ! "
  6. })

Delegates.observable 可观察属性委托

我们把属性委托给Delegates.observable函数,当属性值被重新赋值的时候, 触发其中的回调函数 onChange。

该函数定义如下:

  1. public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):
  2. ReadWriteProperty<Any?, T> = object : ObservableProperty<T>(initialValue) {
  3. override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
  4. }

代码示例:

  1. class PostHierarchy {
  2. var level: String by Delegates.observable("P0",
  3. { property: KProperty<*>,
  4. oldValue: String,
  5. newValue: String ->
  6. println("$oldValue -> $newValue")
  7. })
  8. }

测试代码:

  1. val ph = PostHierarchy()
  2. ph.level = "P1"
  3. ph.level = "P2"
  4. ph.level = "P3"
  5. println(ph.level) // P3

输出:

  1. P0 -> P1
  2. P1 -> P2
  3. P2 -> P3
  4. P3

我们可以看出,属性level每次赋值,都回调了Delegates.observable中的lambda表达式所写的onChange函数。

Delegates.vetoable 可否决属性委托

这个函数定义如下:

  1. public inline fun <T> vetoable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Boolean):
  2. ReadWriteProperty<Any?, T> = object : ObservableProperty<T>(initialValue) {
  3. override fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = onChange(property, oldValue, newValue)
  4. }

当我们把属性委托给这个函数时,我们可以通过onChange函数的返回值是否为true, 来选择属性的值是否需要改变。

代码示例:

  1. class PostHierarchy {
  2. var grade: String by Delegates.vetoable("T0", {
  3. property, oldValue, newValue ->
  4. true
  5. })
  6. var notChangeGrade: String by Delegates.vetoable("T0", {
  7. property, oldValue, newValue ->
  8. false
  9. })
  10. }

测试代码:

  1. ph.grade = "T1"
  2. ph.grade = "T2"
  3. ph.grade = "T3"
  4. println(ph.grade) // T3
  5. ph.notChangeGrade = "T1"
  6. ph.notChangeGrade = "T2"
  7. ph.notChangeGrade = "T3"
  8. println(ph.notChangeGrade) // T0

我们可以看出,当onChange函数返回值是false的时候,对属性notChangeGrade的赋值都没有生效,依然是原来的默认值T0 。

Delegates.notNull 非空属性委托

我们也可以使用委托来实现属性的非空限制:

  1. var name: String by Delegates.notNull()

这样name属性就被限制为不能为null,如果被赋值null,编译器直接报错:

  1. ph.name = null // error
  2. Null can not be a value of a non-null type String

属性委托给Map映射

我们也可以把属性委托给Map:

  1. class Account(val map: Map<String, Any?>) {
  2. val name: String by map
  3. val password: String by map
  4. }

测试代码:

  1. val account = Account(mapOf(
  2. "name" to "admin",
  3. "password" to "admin"
  4. ))
  5. println("Account(name=${account.name}, password = ${account.password})")

输出:

  1. Account(name=admin, password = admin)

如果是可变属性,这里也可以把只读的 Map 换成 MutableMap :

  1. class MutableAccount(val map: MutableMap<String, Any?>) {
  2. var name: String by map
  3. var password: String by map
  4. }

测试代码:

  1. val maccount = MutableAccount(mutableMapOf(
  2. "name" to "admin",
  3. "password" to "admin"
  4. ))
  5. maccount.password = "root"
  6. println("MutableAccount(name=${maccount.name}, password = ${maccount.password})")

输出:

  1. MutableAccount(name=admin, password = root)