7.13 委托(Delegation)

7.13.1 代理模式(Proxy Pattern)

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

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

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

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

1. package com.easy.kotlin;

3. interface JSubject {
4. public void request();
5. }

7. class JRealSubject implements JSubject {
8. @Override
9. public void request() {
10. System.out.println("JRealSubject Requesting");
11. }
12. }

14. class JProxy implements JSubject {
15. private JSubject subject = null;

17. //通过构造函数传递代理者
18. public JProxy(JSubject sub) {
19. this.subject = sub;
20. }

22. @Override
23. public void request() { //实现接口中定义的方法
24. this.before();
25. this.subject.request();
26. this.after();
27. }

29. private void before() {
30. System.out.println("JProxy Before Requesting ");
31. }

33. private void after() {
34. System.out.println("JProxy After Requesting ");
35. }
36. }

38. public class DelegateDemo {
39. public static void main(String[] args) {
40. JRealSubject jRealSubject = new JRealSubject();
41. JProxy jProxy = new JProxy(jRealSubject);
42. jProxy.request();
43. }
44. }

输出:

1. JProxy Before Requesting
2. JRealSubject Requesting
3. JProxy After Requesting
4. ```### 7.13.2 类的委托(Class Delegation)

6. 就像支持单例模式的object对象一样,Kotlin 在语言层面原生支持委托模式。

8. 代码示例:

10. ```kotlin
11. package com.easy.kotlin

13. import java.util.*

15. interface Subject {
16. fun hello()
17. }

19. class RealSubject(val name: String) : Subject {
20. override fun hello() {
21. val now = Date()
22. println("Hello, REAL $name! Now is $now")
23. }
24. }

26. class ProxySubject(val sb: Subject) : Subject by sb {
27. override fun hello() {
28. println("Before ! Now is ${Date()}")
29. sb.hello()
30. println("After ! Now is ${Date()}")
31. }
32. }

34. fun main(args: Array<String>) {
35. val subject = RealSubject("World")
36. subject.hello()
37. println("-------------------------")
38. val proxySubject = ProxySubject(subject)
39. proxySubject.hello()
40. }

在这个例子中,委托代理类 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()

4. override fun toString(): String {
5. return "DelegatePropertiesDemo Class"
6. }
7. }

9. class Content {
10. operator fun getValue(delegatePropertiesDemo: DelegatePropertiesDemo, property: KProperty<*>): String {
11. return "${delegatePropertiesDemo} property '${property.name}' = 'Balalala ... ' "
12. }

14. operator fun setValue(delegatePropertiesDemo: DelegatePropertiesDemo, property: KProperty<*>, value: String) {
15. println("${delegatePropertiesDemo} property '${property.name}' is setting value: '$value'")
16. }
17. }

在 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)

6. val e = DelegatePropertiesDemo()
7. println(e.content) // call Content.getValue
8. e.content = "Confucius" // call Content.setValue
9. 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. })

8. val lazyValueSynchronized1: String by synchronizedLazyImpl
9. println(lazyValueSynchronized1)
10. println(lazyValueSynchronized1)

12. val lazyValueSynchronized2: String by lazy {
13. println("lazyValueSynchronized2  3!")
14. println("lazyValueSynchronized2  2!")
15. println("lazyValueSynchronized2  1!")
16. "Hello, lazyValueSynchronized2 ! "
17. }

19. println(lazyValueSynchronized2)
20. println(lazyValueSynchronized2)

输出:

1. lazyValueSynchronized1  3!
2. lazyValueSynchronized1  2!
3. lazyValueSynchronized1  1!
4. Hello, lazyValueSynchronized1 !
5. Hello, lazyValueSynchronized1 !

7. lazyValueSynchronized2  3!
8. lazyValueSynchronized2  2!
9. lazyValueSynchronized2  1!
10. Hello, lazyValueSynchronized2 !
11. Hello, lazyValueSynchronized2 !

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

1. val synchronizedLazyImpl = lazy({
2. println("lazyValueSynchronized1  3!")
3. println("lazyValueSynchronized1  2!")
4. println("lazyValueSynchronized1  1!")
5. "Hello, lazyValueSynchronized1 ! "
6. })

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

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

如果初始化委托多个线程可以同时执行,不需要同步锁,使用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. })

7. var notChangeGrade: String by Delegates.vetoable("T0", {
8. property, oldValue, newValue ->
9. false
10. })
11. }

测试代码:

1. ph.grade = "T1"
2. ph.grade = "T2"
3. ph.grade = "T3"
4. println(ph.grade) // T3

6. ph.notChangeGrade = "T1"
7. ph.notChangeGrade = "T2"
8. ph.notChangeGrade = "T3"
9. 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. ))

6. 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. ))

6. maccount.password = "root"
7. println("MutableAccount(name=${maccount.name}, password = ${maccount.password})")

输出:

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