Minecraft Spigot PluginにKoinを導入する

# 背景

プラグイン開発において、pluginのインスタンスや、serverのインスタンスの呼び出しや参照は頻繁に使います。

メタデータでのJavaPluginの参照

```kotlin
fun setMetadata(player: Player, plugin: JavaPlugin, key: String, value: Boolean) {
    player.setMetadata(key, FixedMetadataValue(plugin, value))
}
```

プレイヤーの取得

```kotlin
class PlayerListener(val plugin: JavaPlugin) : Listener {
    @EventHandler
    fun onPlayerJoin(event: PlayerJoinEvent) {
        val players = plugin.server.onlinePlayers
    }
}
```

```kotlin
class Game(val plugin: JavaPlugin) {
    fun start() {
        val players = plugin.server.onlinePlayers
    }
}
```

といったように、pluginインスタンスをバケツリレーのようにして受け渡すことが多いです。

## 既存の解決策

シングルトンとして定義する方法

```SamplePlugin.kt
class SamplePlugin : JavaPlugin() {

    companion object {
        lateinit var INSTANCE: JavaPlugin
            private set
    }

    override fun onEnable() {
        PLUGIN = this
    }
}

// 参照するとき
fun example() {
    val plugin = SamplePlugin.INSTANCE
}
```

上記のように定義して、コードのどの位置からでも参照できる方法があります。
この方法でも大きな問題はなさそうですが、以下のような課題点があります。

- pluginのインスタンスに対して再代入が起きる可能性がある
- テストが難しくなる
- コードとしての見通しが悪くなる


# Koinの導入

Spigotプラグインのpluginやserverといったインスタンスの参照をKoinを使ってわかりやすく記述することができます。
一般的に、Dependency Injectionという方法になります。

build.gradleに以下を追加して記述します。


```build.gradle
buildscript {
    ext {
        koin_version = '2.1.6'
    }
    repositories {
        jcenter()
    }
    dependencies {
        classpath "org.koin:koin-gradle-plugin:$koin_version"
    }
}

apply plugin: 'koin'

dependencies {
    compile "org.koin:koin-core:$koin_version"
    compile "org.koin:koin-core-ext:$koin_version"
    testCompile "org.koin:koin-test:$koin_version"
}
```

JavaPluginを以下のように書きます。

```SamplePlugin.kt
class SamplePlugin : JavaPlugin() {
    override fun onEnable() {
        // .....

        setupKoin()
    }

    private val myModule = module {
        single<JavaPlugin> { this@SamplePlugin }
        single { server }
    }

    private fun setupKoin() {
        startKoin {
            modules(listOf(myModule))
        }
    }
}
```

myModuleで定義したものは、以下のようなKoinComponentを継承したクラスで参照できます。

```Game.kt
class Game : KoinComponent {
    private val plugin: JavaPlugin by inject()
    private val server: Server by inject()

    fun start() {
        val players = server.onlinePlayers
    }
}
```

また、自分で作成したクラスもKoinのmoduleに含めることができます。

```PluginPreference.kt
class PluginPreference(private val plugin: JavaPlugin) {
    // .....
}
```

```SamplePlugin.kt
class SamplePlugin : JavaPlugin() {
    override fun onEnable() {
        // .....

        setupKoin()
    }

    private val myModule = module {
        single<JavaPlugin> { this@SamplePlugin }
        single { server }
        single { PluginPreference(get()) }
    }

    private fun setupKoin() {
        startKoin {
            modules(listOf(myModule))
        }
    }
}
```

`get()`を使うことで、Koinで定義したオブジェクトを参照することができます。


# テストの記述

テストを行うときは、[MockK](https://mockk.io/)で生成したモックオブジェクトをKoinに入れることができます。
これによってテストを行いやすくなります。

```GameTest.kt
internal class GameTest {
    companion object {
        private val plugin = mockk<JavaPlugin>()
        private val server = mockk<Server>()

        private val mockModule: Module = module {
            single { plugin }
            single { server }
        }

        @BeforeAll
        @JvmStatic
        internal fun beforeAll() {
            startKoin {
                modules(listOf(mockModule))
            }
        }
    }

    @Test
    fun result() {
        // テストを記述する
        val game = Game()
        Assertions.assertEquals()
    }
}
```


# 終わりに
KoinやDIコンテナ、依存性の注入については他にも詳しい解説があるので、調べてみてください。