[Kotlin] インターフェースの委任によるjava.time.Clockの利用

:::note warn
(2021/12/24追記) 
この記事に記載の方法は、有効活用できる場面がどうも限定的であるようです。
詳細は文末に追記しますが、愚直にjava.time.Clockをメンバ変数に追加する方法が、いちばん使いまわしが良さそうです。
:::

# プログラマはザ・ワールドが使いたい
**現在日時**を取得利用するプログラムでは、

- **テストのときは取得日時を固定したい**

というありふれた要望がつきまとう。
**Kotlin**で日時を扱うなら**java.timeライブラリ**を使うことが多いが、java.timeライブラリで『テストのときは日時固定』を実現する場合、ちょっと面倒だ。

実現方法について、いくつかのサイトは `java.time.Clock` を現在日時の取得時に渡せばOK、と案内している[^1]。

```kotlin
// import java.time.Clock
class Something(
    val clock: Clock // DIとかでもってくる
) {
    fun doSomething() {
        val today = LocalDate.now(clock) // clockのインスタンスを渡す
        //...後略
    }
}
```
**方法としては正しそう**…だけど、このインスタンスを参照するためのフィールドを、すべての現在日時の利用箇所に追加するのは、あまりスマートではなさそうである。

ところで**Kotlin**には**interfaceの委任(interface delegation)**という[テクニックがある](https://developer.android.com/codelabs/kotlin-bootcamp-classes)。
これを利用して、別のアプローチがとれないか考えてみる。

# 委任してみる

#### 1. interfaceと委任オブジェクトをつくる

```kotlin
// import java.time.Clock
interface ClockReference {
    val clock: Clock
}

class ClockProvider : ClockReference, KoinComponent {
    override val clock: Clock by inject()
}
```
`java.time.Clock`型のフィールドを持つ*interface*を定義し、そのインターフェースを実装した*class*を作る[^2]。
objectは`Clock`型のフィールドを*override*する。初期化は**DI**でおこなう。

サンプルでは、DIフレームワークとして[Koin](https://insert-koin.io/)を使用する。
`KoinComponent`の実装は、依存注入の対象であることの宣言。*by inject()* は、DIにより依存を注入することの宣言。


#### 2. Clockの利用クラスでinterfaceの実装を宣言、実装を委任
```kotlin
interface ISomething {
    fun doSomethingWithDate()
}

class Something : ISomething, ClockReference by ClockProvider() {
    override fun doSomethingWithDate() {
        val today = LocalDate.now(clock) // ClockReferenceのフィールドを参照
        println("Today is $today. Everything is fine.")
    }
}
```
Clockの利用クラスに`ClockReference`の実装と、**実装の委任**を宣言する(`ClockReference by ClockProvider()`)。
これでフィールドを追記することなく、Clock型のインスタンスを自由に扱えるようになる。

:::note info
Clock型のインスタンスは、ClockProvider経由でDIで提供される。
テスト用のインスタンスをDIコンテナに登録すれば、時間を固定したClockや、Mockk等のライブラリによるMockを差し込むことが可能になる。
:::

<details><summary>動作確認コード</summary><div>

```kotlin
fun main() {
    prepareDi() // diコンテナにinstance登録。詳細は割愛

    val something: ISomething = Something()
    something.doSomethingWithDate()
    // 実行結果：Today is {実行日}. Everything is fine.
}
```
[コード全体](https://github.com/satotats/kotlin-training/blob/master/src/main/kotlin/com/satotats/exercise/kotlin/clock/clockDelegationSample.kt)
</div></details>


# 気をつけたいポイント
- `ClockReference`の実装クラス(前掲の例でいう*Something*)から**Clockへの依存が見えにくい**。  
  - もともと*LocalDate.now()*や*System.currentTimeMillis()*などの利用に制限がなかったなら、もともとシステム内時計への依存はスルーしていたわけなので、この場合はあまり問題ではなさそう。他方、暗黙裡の依存は基本的に排除する方針なら、この方法は適合しない。
- `ClockReference`の実装クラスは、**クラス外部にclockフィールドが露出する**。  
  - インターフェース上のフィールドは**public**でしか宣言できないためだ。Clockのインスタンスは最低限かつimmutableなApiしか持たないので、実装クラス外からの参照も致命傷にはならなそうではある。このまま採用できるかは、アプリの開発方針だとかによる。
- `ClockProvider`は**特定のDIフレームワークに依存する**。  
  - …ただ、DIフレームワークが変わったとして、`ClockReference`の利用クラスに変更の影響は殆どないはずではある。利用クラスは**ClockReference上**の*clock*フィールドを利用するだけであって、フィールドがどのように提供されるかには関知しないからだ。とはいえ、DIに依存したクラスが、DIと分離されたクラスに登場するとヒヤリとする。この採用可否も、開発方針による。

# むすび
- **テストで時間止めてえな**、と調べて読んだ『[java.time.Clockで現在時刻を扱うUT](https://qiita.com/asmasa/items/12ecfc29a380ced48f06)』に触発されて書きました。  
 (minimal)cake pattern、として披露できるほど理解できていないので、[Kotlin Bootcamp](https://developer.android.com/courses/kotlin-bootcamp/overview)で利用されていた「インターフェースの委任」というワードに逃げました。
- `Clock`はいろんなところで暗黙裡に利用されていたりするので、テスト時のClockのモッキングにはしばしば難があります(例：apiの呼び出し回数が把握しにくい)。  
Clockのフィールドをinterface上に配置する代わりに、*now()*などの取得結果を返すApiをインターフェース上に整備するのも一案かもしれません。
- (書いといてアレですが)デメリットも目立つので、**普通にフィールド足したらエエんとちゃうのォ～**という気持ちの間で揺れています。ｺﾞｲｹﾝｵﾏﾁｼﾃﾏ:innocent:

### 当座の運用を経て (2021/12/24追記)
Clockを**『普通にフィールド(コンストラクタ引数)に足す』**のが、いちばん使いまわしやすそうです。

この記事の方法だと、ClockProviderの初期化がDI以外でできません。
テスト時にも同じDIを使うなら良いですが、別の方法でモックを差し込みたい場合、導線がないので詰みます。
得られるメリットにたいして、失う拡張性が大きすぎるように思います。

記事執筆時にDIアリのテストばかり書いており、一見アタリマエながら気づけませんでした。先んじて詰んだかた、万一いたら申し訳ない…。
Clockのスマートな扱いかたでこんなのあるよ、というのあればぜひぜひお知らせください…🙏
もし万一この方法に活路があるなら、その情報もぜひ。

[^1]: たとえば：[stackoverflow](https://stackoverflow.com/questions/2001671/override-java-system-currenttimemillis-for-testing-time-sensitive-code)
[^2]: *object*で実装してしまうと、たとえば自動テストを連続実行する場合に不都合が発生する。objectで生成されるシングルトンなインスタンスが、複数のテストで共有されてしまうのだ。テストごとにモックを差し替えるなど、基本的なニーズにこたえられない。