［Kotlin］`by` で 委譲する仕組みを理解する

# はじめに

Kotlin には `by` を使うことで委譲を簡単に実装することができます。
今回はこの仕組みについて理解して内容をまとめたいと思います。

# 委譲とは？

そもそも委譲についてなのですが Wikipedia では次のように定義されています。
つまり委譲とは`あるオブジェクトの操作を一部他のオブジェクトに代替させる手法`です。

```
委譲 (英: delegation) とはオブジェクト指向プログラミングにおいて、
あるオブジェクトの操作を一部他のオブジェクトに代替させる手法のこと。
```

次のコードは `Windows`の操作を `Rectangle`に委譲している例になります。
`Window`の `name` や `area()`で `Rectangle`の `name` `area()`を呼び出し、
`Window`が実装すべき操作を `Rectangle`にやらせています。
このような時 `Window`の 操作を `Rectangle`に委譲していると言われます。

```kotlin
interface ClosedShape {
    val name : String
    fun area(): Int
}

class Rectangle(override val name : String, val width: Int, val height: Int) : ClosedShape {
    override fun area() = width * height
}

class Window(private val bounds: ClosedShape) : ClosedShape {
    // Delegation
    override val name = bounds.name
    override fun area() = bounds.area()
}

fun main(args: Array<String>) {
    val rect = Rectangle("rectanble", 10, 10)
    val window = Window(rect)
    println(window.name)
    println(window.area())
}
```

# 委譲するのは手間がかかる

委譲するために`あるオブジェクトから他のオブジェクト`を呼び出す必要があります。
そのため委譲するメソッドが増えると、他のオブジェクトを呼び出す処理も増えていくことになります。

呼び出し処理が増えていくと、コードの見通しも悪くなっていく問題が発生します。
また呼び出し処理を手作業で記述していくのは面倒で手間がかかります。

```kotlin

interface ClosedShape {
    val name : String
    fun area(): Int
    fun new1()
    fun new2()
    fun new3()
}

class Rectangle(override val name : String, val width: Int, val height: Int) : ClosedShape {
    override fun area() = width * height
    override fun new1() { println("call new1") }
    override fun new2() { println("call new2") }
    override fun new3() { println("call new3") }
}

class Window(private val bounds: ClosedShape) : ClosedShape {
    // Delegation
    override val name = bounds.name
    override fun area() = bounds.area()
    override fun new1() { bounds.new1() }
    override fun new2() { bounds.new2() }
    override fun new3() { bounds.new3() }
}

fun main(args: Array<String>) {
    val rect = Rectangle("rectanble", 10, 10)
    val window = Window(rect)
    println(window.name)
    println(window.area())
    window.new1()
    window.new2()
    window.new3()
}

----- OUTPUT -----
rectanble
100
call new1
call new2
call new3
```

# `by` を使って簡単に操作を委譲しよう

他のオブジェクトへの委譲を手作業やるとものすごく手間がかかります。
なので Kotlin では `by` を利用することで操作を他のオブジェクトに委譲できるようになっています。

やり方は簡単で `Window` の `ClosedShape` に `by <インスタンス名称>` を追加するだけです。
すると`Window`の `ClosedShape` の操作を `by` で指定した`インスタンス` に全て委譲できます。

```kotlin
interface ClosedShape {
    val name : String
    fun area(): Int
    fun new1()
    fun new2()
    fun new3()
}

class Rectangle(override val name : String, val width: Int, val height: Int) : ClosedShape {
    override fun area() = width * height
    override fun new1() { println("call new1") }
    override fun new2() { println("call new2") }
    override fun new3() { println("call new3") }
}

class Window(private val bounds: ClosedShape) : ClosedShape by bounds

fun main(args: Array<String>) {
    val rect = Rectangle("rectanble", 10, 10)
    val window = Window(rect)
    println(window.name)
    println(window.area())
    window.new1()
    window.new2()
    window.new3()
}

----- OUTPUT -----
rectanble
100
call new1
call new2
call new3
```

というように `by` を利用すると `インタフェースで実装すべき操作`を`他のオブジェクト`へ簡単に委譲できます。
次のように 2つのインタフェースに対して処理を委譲することも可能になっているので、
多くのメソッド・プロパティを持つ複数のインタフェースの処理を委譲していときはすごく便利です。

```Kotlin
interface ButtonController {
    fun up()
    fun down()
    fun right()
    fun left()
    fun a()
    fun b()
    fun plus()
    fun minus()
    fun home()
    fun one()
    fun two()
}

interface  GyroController {
    fun xyz(x : Float, y : Float, z : FXMLLoader)
}

class WiiController(private val button : ButtonController, private val gyro: GyroController) 
: ButtonController by button, GyroController by gyro
}
```

# おわりに

本記事の学びは次の3つになると思います。
Kotlin であれば委譲する時には迷わず `by` を使うのが良さそうです。

- 委譲とは`あるオブジェクトの操作を一部他のオブジェクトに代替させる手法`である
- 委譲では`あるオブジェクトの操作を他のオブジェクトに代替させる`ため冗長な記述が増えてしまいがち
- だが Kotlinの `by` を利用することで冗長な記述をせず、簡単に操作を委譲させることができる。

# 参考文献
- [Delegation | Kotlin Reference](https://kotlinlang.org/docs/reference/delegation.html)
- [Delegation Pattern](https://en.wikipedia.org/wiki/Delegation_pattern)