LoginSignup
3
0

More than 1 year has passed since last update.

お手軽なコンポーネント依存性管理方法

Posted at

はじめに

ちょっとしたアプリやライブラリを作っていて、コンポーネントの依存性を管理したくなる時があります。例えば、「あるコンポーネントは毎回作成したいけど他のコンポーネントはシングルトンにしたい。そして、それらのコンポーネント間に依存関係がある。」と言った場合です。

もちろん、世の中には依存性管理のためのライブラリがいくつもあるのでそれらを活用するのも良いでしょう。しかし、わざわざライブラリを導入するほどでもないということも多いかと思います。そんな時に私が使っているお手軽な方法を紹介したいと思います。

前提

Controller、Service、Repositoryという3つのコンポーネントがあって、それらに依存関係があるとします。なお、Controllerは毎回新規のインスタンスが生成され、ServiceとRepositoryはシングルトンとして扱われるものとします。

シーケンス図で表すとこんな感じでしょうか。

+------------+    +------------+    +------------+  
| Controller |    |  Service   |    | Repository |  
+-----+------+    +-----+------+    +-----+------+  
      |                 |                 |         
      |    execute()    |                 |         
      +---------------->|    execute()    |         
      |                 |---------------->|         
      |                 |<----------------+         
      |<----------------+                 |          

コードも示してみます。

各コンポーネントの中では、自身の文字列表現と後続のコンポーネントの実行結果を連結しています。これは、呼び出しごとに新インスタンスが生成されているのか、同じインスタンスが使われているのか後ほど確認するためです。

Controller
class Controller(private val service: Service) {
    fun execute(): String {
        return this.toString() + " -> " + service.execute()
    }
}
Service
class Service(private val repository: Repository) {
    fun execute(): String {
        return this.toString()+ " -> " + repository.execute()
    }
}
Repository
class Repository {
    fun execute(): String {
        return this.toString()
    }
}

解決方法

私はこんな感じのコードを書いてコンポーネントの依存関係を管理します。

Container
object Container {
    // 呼ばれるたびに新規インスタンス作成
    val controller get() = Controller(service)
    // シングルトン
    val service by lazy { Service(repository) }
    // シングルトン
    val repository by lazy { Repository() }
}

ポイント

このクラスのポイントは3つです。

  • 依存先コンポーネントはプロパティ経由で得る
  • get()を使うことでControllerのインスタンスは毎回生成
  • lazyを使うことでSeriverとRepositoryのインスタンスは1度だけ生成(実質的にシングルトン)

1度だけだ生成するならlazyを使う代わりにval service = Service(repository)のようにプロパティのinitializerを使えばいいのではないかと思ったかもしれません。しかし、これはうまくいかないのです。参照しているrepositoryプロパティはまだ宣言されていないのでコンパイルエラーになります。もちろん先にrepositoryを宣言した上でserviceプロパティを宣言すればinitializerを使う方法でも構わないのですが、lazyを使うことでプロパティの宣言順序を気にする必要がなくなります。

Containerの利用例

上述のContainerを利用してみましょう。

main
fun main() {
    val controller1 = Container.controller
    println(controller1.execute())

    val controller2 = Container.controller
    println(controller2.execute())
}

実行結果はこんな感じになります。

example.Controller@6ce253f1 -> example.Service@53d8d10a -> example.Repository@e9e54c2
example.Controller@65ab7765 -> example.Service@53d8d10a -> example.Repository@e9e54c2

以下のことがわかりますね。

  • Controllerは1度目と2度目の呼び出しで異なるインスタンス
  • ServiceとRepositoryは1度目と2度目の呼び出しで同じインスタンス

おわりに

get()lazyなど、標準で組み込まれている機能を使うことでコンポーネントの依存性管理が簡単にできることを紹介しました。

もし良ければお試しください。
この方法ではうまくいかないケースがあるなどのフィードバックも歓迎です。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0