DIコンテナとService Locator


本記事について

DIコンテナとService Locatorの違いを整理するために本記事を書きます。
初めに、DIコンテナとService Locatorの説明を行い、その後違いについて説明します。
DIについては DIを出来る限りシンプルに紹介する - Qiita を参照ください。

DIコンテナとは

DIコンテナはDIで依存性を解決するオブジェクトを管理するオブジェクトです。
ClientクラスがServiceクラスに依存する場合にServiceオブジェクトの登録と提供を行います。

DIコンテナを使用しない場合は以下のようになります。

class Client(private val service: ServiceInterface) {
    fun search(): String {
        return service.request()
    }
}

interface ServiceInterface {
    fun request(): String
}

class Service: ServiceInterface {
    override fun request(): String {
        return "requested"
    }
}

fun main(args: Array<String>) {
    val x = Client(Service()).search()
    println(x)
}

DIコンテナを使うとこのようになります。
コンテナの実装は一番簡易な方法を使っています。
コンテナの実装方法はいろいろあるので本記事では省略します。

class Client(private val service: ServiceInterface) {
    fun search(): String {
        return service.request()
    }
}

interface ServiceInterface {
    fun request(): String
}

class Service: ServiceInterface {
    override fun request(): String {
        return "requested"
    }
}

val diContainer = mapOf("s" to Service())

fun main(args: Array<String>) {
    val service = diContainer["s"]!!
    val x = Client(service).search()
    println(x)
}

DIコンテナを使用することで複数箇所で依存するオブジェクトを参照するときに同じオブジェクトに依存することができます。

Service Locatorとは

DIコンテナを使うパターンで誤ってService Locatorになってしまう場合があります。
以下のコードはService Locatorと呼ばれるパターンです。

class Client {
    private val service = diContainer["s"]!!
    fun search(): String {
        return service.request()
    }
}

interface ServiceInterface {
    fun request(): String
}

class Service: ServiceInterface {
    override fun request(): String {
        return "requested"
    }
}

val diContainer = mapOf("s" to Service())

fun main(args: Array<String>) {
    val x = Client().search()
    println(x)
}

DIとの違いはClientクラスがDIコンテナに依存しているかどうかです。

Service Locatorのイマイチな点

Service Locatorのイマイチな点を理解するにはDI導入の流れとクラス図の変化を見ていくとわかりやすいです。

まずはDIのない設計

DIをするためにInterfaceに依存する設計

これでClientからServiceへの依存がなくなりました

DIコンテナを使用した設計

Service Locatorの設計

Service LocatorパターンではClientからServiceへの依存関係をへらすためにDIContainerへの依存関係が増えてしまう点がイマイチな点です。

DIコンテナのアンチパターン

最後にDIコンテナのアンチパターンについて紹介します。

  • Service Locator
    • DIコンテナを使っているつもりがService Locatorになる場合があります
    • 上記の通り依存関係が増えるためアンチパターンにしています
  • DIコンテナに登録するオブジェクトがmutable
    • DIコンテナはグローバルオブジェクトと同じためmutableなオブジェクトを登録すると管理が難しくなります