読者です 読者をやめる 読者になる 読者になる

算譜王におれはなる!!!!

偏りはあると思うけど情報技術全般についてマイペースに書くよ。

KotlinでDIして遊ぶ

Kotlin プログラミング

Kotlin Advent Calendar 2013 10日目のエントリです。

依存性注入したい

タイトルの通り今回はKotlinでDIに挑戦。DIコンテナ使うんでなく素のKotlinでやってみる。難しいことをやろうとしているのではなく、責任外の処理を別のオブジェクトに委譲してユニットテストをやり易くしたいだけ。

まずは準備。

data class Employee(
    val firstName: String,
    val lastName: String
)

サンプルでありがちなEmployeeクラス。dataアノテーションについてはこちらのエントリを参照。

trait EmployeeRepository {
    fun save(employee: Employee): Employee
}

で、次にDAO的なトレイトを定義。

ここでcreateEmployeeメソッドを持ったEmployeeServiceクラスを作りたい。このクラスはEmployeeRepository#saveを使う。JavaだったらEmployeeServiceコンストラクタEmployeeRepositoryインスタンスを受け取ってフィールドに保持する感じになると思う。Kotlinはちょっと違う。

class EmployeeService(employeeRepos: EmployeeRepository) : EmployeeRepository by employeeRepos {
    fun createEmployee(firstName: String, lastName: String): Employee {
        return save(Employee(firstName, lastName))
    }
}

1行目の: EmployeeRepository by employeeReposに注目。これによりクラスはEmployeeRepositoryのサブタイプになり、そのメソッドへのアクセスはコンストラクタで受け取ったEmployeeRepositoryインスタンスへ自動的に委譲される。微妙な違いだけど、emplyoeeRepos.saveという記述ではなく単にsaveとしてメソッドを呼び出しており、余計なプロパティを持つ必要がなくなる。

実際に動かしてみる。EmployeeRepositoryの実装として次のクラス(というかオブジェクト)を定義する。

object DummyEmployeeRepository: EmployeeRepository {
    override fun save(employee: Employee): Employee {
        println("saved: " + employee)
        return employee
    }
}

EmployeeServiceへ依存性を注入して実際に使う。

fun main(args: Array<String>) {
    val employeeService = EmployeeService(DummyEmployeeRepository)
    employeeService.createEmployee("Taro", "Nagasawa")
}

期待通りsaved: Employee(firstName=Taro, lastName=Nagasawa)と出力されるはず。

最後に、せっかくDIできるようになったんで簡単なテストを書く。

import org.junit.Test as test
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.times

class EmployeeServiceTest {

    test fun 指定された名前の従業員を新たに生成し保存すること() {
        val employeeRepos = mock(javaClass<EmployeeRepository>())!!

        EmployeeService(employeeRepos).createEmployee("Taro", "Nagasawa")

        verify(employeeRepos, times(1))!!.save(Employee("Taro", "Nagasawa"))
    }
}

おまけ

すみません、釣りなタイトルになってしまった。。

と思ったらこんなツイートもいただきました。