Kotlin中的by关键字

参考链接

装饰器模式

装饰器模式又名包装模式,来以对客户透明的方式(客户端并不觉得修饰前后的区别)扩展对象的功能,是继承关系的一种替代方案。

在装饰模式中的角色有:

  • 抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
  • 具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。
  • 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
  • 具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。
interface CanBeWorn{
    void worn();
}

class UnderWear implements CanBeWorn{

    @Override
    public void worn() {
        System.out.print("穿内衣");
    }
}

/**
    * 装饰器模式的核心
    * 它的子类都可以代理执行注入类的worn()方法
    */
class Mother implements CanBeWorn{

    private CanBeWorn canBeWorn;

    public Mother(CanBeWorn canBeWorn){
        this.canBeWorn = canBeWorn;
    }

    @Override
    public void worn() {
        canBeWorn.worn();
    }
}

class Coat extends Mother{

    public Coat(CanBeWorn canBeWorn) {
        super(canBeWorn);
    }

    @Override
    public void worn() {
        super.worn();
        System.out.println("穿上衣");
    }
}

class Pants extends Mother{

    public Pants(CanBeWorn canBeWorn) {
        super(canBeWorn);
    }

    @Override
    public void worn() {
        super.worn();
        System.out.println("穿裤子");
    }
}

这个例子举的有些不太符合,但是基本上能说明装饰模式的过程。

CanBeWorn canBeWorn = new UnderWear();
CanBeWorn coat = new Coat(canBeWorn);
CanBeWorn pants = new Pants(coat);

装饰模式的简化

大多数情况下,装饰模式的实现都要比上面给出的示意性例子要简单。

如果只有一个ConcreteComponent类,那么可以考虑去掉抽象的Component类(接口),把Decorator作为一个ConcreteComponent子类。

class UnderWear{

    public void worn() {
        System.out.print("穿内衣");
    }
}

class Mother extends UnderWear{

    private UnderWear underWear;

    public Mother(UnderWear underWear){
        this.underWear = underWear;
    }

    @Override
    public void worn() {
        underWear.worn();
    }
}

class Coat extends Mother{

    public Coat(UnderWear underWear) {
        super(underWear);
    }

    @Override
    public void worn() {
        super.worn();
        System.out.println("穿上衣");
    }
}

class Pants extends Mother{

    public Pants(UnderWear underWear) {
        super(underWear);
    }

    @Override
    public void worn() {
        super.worn();
        System.out.println("穿裤子");
    }
}

类的代理

上面的代码用Kotlin实现

interface CanBeWorn {
    fun worn()
}

class UnderWear : CanBeWorn {

    override fun worn() {
        println("穿内衣")
    }
}

abstract class Mother(val canBeWorn: CanBeWorn) : CanBeWorn {

    override fun worn() {
        canBeWorn.worn()
    }
}

class Coat(canBeWorn: CanBeWorn) : Mother(canBeWorn) {

    override fun worn() {
        super.worn()
        println("穿上衣")
    }
}

class Pants(canBeWorn: CanBeWorn) : Mother(canBeWorn) {

    override fun worn() {
        super.worn()
        println("穿裤子")
    }
}

fun main(args: Array<String>){
    Pants(Coat(UnderWear())).worn()
}

在Kotlin中可以使用by关键字将接口的实现委托到另一个对象。

所以上面的

abstract class Mother(val canBeWorn: CanBeWorn) : CanBeWorn {

    override fun worn() {
        canBeWorn.worn()
    }
}

可以修改如下

abstract class Mother(val canBeWorn: CanBeWorn) : CanBeWorn by canBeWorn

覆盖代理成员

编译器会使用 override 覆盖的实现而不是委托对象中的。

interface Base {
    fun printMessage()
    fun printMessageLine()
}

class BaseImpl(val x: Int) : Base {
    override fun printMessage() { print(x) }
    override fun printMessageLine() { println(x) }
}

class Derived(b: Base) : Base by b {
    override fun printMessage() { print("abc") }
}

fun main() {
    val b = BaseImpl(10)
    Derived(b).printMessage()
    Derived(b).printMessageLine()
}

>>> abc10

注意,以这种方式重写的成员不会在委托对象的成员中调用 ,委托对象的成员只能访问其自身对接口成员实现:

interface Base {
    val message: String
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override val message = "BaseImpl: x = $x"
    override fun print() { println(message) }
}

class Derived(b: Base) : Base by b {
    // 在 b 的 `print` 实现中不会访问到这个属性
    override val message = "Message of Derived"
}

fun main() {
    val b = BaseImpl(10)
    val derived = Derived(b)
    derived.print()
    println(derived.message)
}

>>> BaseImpl: x = 10
>>> Message of Derived

属性委托

不仅可以类代理,还可以代理类属性,监听属性变化。

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

语法是: val/var <属性名>: <类型> by <表达式>。在 by 后面的表达式是该 委托, 因为属性对应的 get()(与 set())会被委托给它的 getValue() 与 setValue() 方法。 属性的委托不必实现任何的接口,但是需要提供一个 getValue() 函数(与 setValue()——对于 var 属性)。 例如:

class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me!"
    }
 
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$value has been assigned to '${property.name}' in $thisRef.")
    }
}

当我们从委托到一个 Delegate 实例的 p 读取时,将调用 Delegate 中的 getValue() 函数, 所以它第一个参数是读出 p 的对象、第二个参数保存了对 p 自身的描述 (例如你可以取它的名字)。 例如:

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

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

标准委托

延迟属性lazy

lazy() 是接受一个 lambda 并返回一个 Lazy 实例的函数,返回的实例可以作为实现延迟属性的委托: 第一次调用 get() 会执行已传递给 lazy() 的 lambda 表达式并记录结果, 后续调用 get() 只是返回记录的结果。

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

fun main() {
    println(lazyValue)
    println(lazyValue)
}

>>> computed!
>>> Hello
>>> Hello

可观察属性 Observable

Delegates.observable() 接受两个参数:初始值与修改时处理程序(handler)。 每当我们给属性赋值时会调用该处理程序(在赋值后执行)。它有三个参数:被赋值的属性、旧值与新值:

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

fun main() {
    val user = User()
    user.name = "first"
    user.name = "second"
}

>>> <no name> -> first
>>> first -> second