【Kotlin】「委譲による実装」と、外部から操作されないための工夫

## 概要

- あるインターフェイスを実装するクラスを作成する際に「委譲による実装」を利用すると楽になることがある。
- ただし公式リファレンスにある基本のやり方そのままだと、クラス内に隠蔽されるべきオブジェクトを、クラスの外から操作できてしまう場合がある。
- この問題はセカンダリコンストラクタを使った工夫をすることで回避できる。

## 課題

Fizz Buzz を行うクラスを実装してみましょう。
次のようなクラスです。

- [`List<String>`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html) インターフェイスを実装しており、`["1", "2", "Fizz", "4", "Buzz", ...]` というような要素を持つ。
- 要素は最初は空であり、`next()` を呼び出すごとに1つずつ増える。  
  `[]` —`next()`→ `["1"]` —`next()`→ `["1", "2"]` —`next()`→ ...

使用例は次のようになります。

```Kotlin
fun main() {
    val fizzBuzz = FizzBuzz()
    repeat(15) { fizzBuzz.next() } // 15までの要素を生成する。
    fizzBuzz.forEach { print("$it, ") } // > 1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz,
}
```

どのように実装するとよいでしょうか。

あらかじめ、与えられた数値を Fizz Buzz した文字列に変換する関数だけ作っておきましょう。

```Kotlin
fun fizzBuzz(number: Int): String = when {
    number % 3 == 0 && number % 5 == 0 -> "FizzBuzz"
    number % 3 == 0 -> "Fizz"
    number % 5 == 0 -> "Buzz"
    else -> number.toString(10)
}
```

## 失敗例

### 案１

「`List<String>` を実装しとるクラスを継承したろ！」

```Kotlin
class FizzBuzz1 : ArrayList<String>() { // List<String> インターフェイスを実装した ArrayList<String> クラスを継承。
    fun next() {
        this += fizzBuzz(size + 1)
    }
}
```

「あかん、よそから勝手な値を突っ込めてまう」

```Kotlin
fun main() {
    val fizzBuzz = FizzBuzz1()
    fizzBuzz += "FooBar"
    fizzBuzz.forEach { print("$it, ") } // > FooBar, 
}
```

### 案２

「ほな、 `List<String>` インターフェイスを実装しよか」

```Kotlin
class FizzBuzz2 : List<String> {
}
```

「`List<String>` のメンバを全部自前で実装するんはさすがに面倒やな。なかで `mutableListOf()` 使（つこ）たろか」

```Kotlin
class FizzBuzz2 : List<String> {
    private val mutableList: MutableList<String> = mutableListOf()
}
```

「`List<String>` インターフェイスのメンバが呼ばれたら `mutableList` の同名のメンバを呼ぶようにすればええ。こないなふうに…」

```Kotlin
class FizzBuzz2 : List<String> {
    private val mutableList: MutableList<String> = mutableListOf()

    override val size: Int
        get() = mutableList.size

    override fun get(index: Int): String = mutableList.get(index)
    
    ...

}
```

「…ってちょお待てや、何個あんねん！[^11]　やっとれるかい！」

[^11]: 11個

## 委譲による実装

こういうときに、「[委譲による実装](https://kotlinlang.org/docs/reference/delegation.html)」機能が役に立ちます。

### 案３

```Kotlin
class FizzBuzz3(
    private val mutableList: MutableList<String> // コンストラクタ引数で MutableList<String> のインスタンスを受け取る。
): List<String> by mutableList { // List<String> のメンバが呼び出されたら mutableList の同じメンバに処理を委譲することを宣言。
    fun next() {
        mutableList += fizzBuzz(size + 1)
    }
}
```

コンストラクタ引数で `List<String>` インターフェイスを継承した `MutableList<String>` のインスタンス `mutableList` を受け取り、
クラスが `List<String>` インターフェイスを実装していることを宣言しているところに `by mutableList` を付けています。
こうすることで、`List<String>` のメンバの実装として `mutableList` が使用されます。（案２でやろうとしたように。）

#### 案３の欠点

上記は[公式リファレンス](https://kotlinlang.org/docs/reference/delegation.html)にも書かれているやりかたです。

ですが…。
これは `mutableList` オブジェクトをコンストラクタ引数で受け取っています。
つまり、外から `mutableList` を、ひいては `FizzBuzz3` オブジェクトを、操作することができてしまうのです。

```Kotlin
fun main() {
    val mutableList = mutableListOf<String>()
    val fizzBuzz = FizzBuzz3(mutableList)
    mutableList += "FooBar"
    fizzBuzz.forEach { print("$it, ") } // > FooBar, 
}
```

（あと、使う側が `mutableList` を用意してやらないといけないのがめんどくさい。）

### 案４（完成）

これを解決するにはどうしたらよいか。

- プライマリコンストラクタを `private` にし、
- セカンダリコンストラクタで `mutableList` を生成してプライマリコンストラクタに渡しましょう。

（コンストラクタの書き方について詳しくない方はこちらをご覧ください。→[【Kotlin】コンストラクタ　−　プライマリとセカンダリ](https://qiita.com/sdkei/items/4b773b662e34214b5261)）

```Kotlin
class FizzBuzz private constructor(
    private val mutableList: MutableList<String>
) : List<String> by mutableList {
    constructor() : this(mutableListOf())

    fun next() {
        mutableList += fizzBuzz(size + 1)
    }
}
```

これで `mutableList` をクラス内に隠蔽し、外部から操作されることがなくなりました！
（使う側が `mutableList` を用意する必要もなくなりました！）