そもそも依存性とは?
DIは『依存性注入』と訳されるように、『依存性』がキーワードとなっています。そこで、まず最初に理解すべきなのは、『そもそも依存性って何?』ということです
たとえば、クラスBのインスタンスを作成する際に、クラスAのインスタンスが必要となる場合を考えてみましょう。クラスAのインスタンスから得られるプロパティやメソッドを、クラスBの内部で利用したい場合などが考えられますね。
イメージとしては下の図のような関係になります。

この状況を下のように順序立てて整理すると、依存性とは何かが見えてきます。
- クラスBのインスタンスを作成するにはクラスAのインスタンスが必要
- クラスBはクラスA(とそのインスタンス)の存在無しに役割を果たせない
- クラスBはクラスAに依存性がある(依存関係が成立する)
「そんなの当たり前じゃないか」と思うかもしれませんが、この『当たり前』をしっかり理解しておくことが、DIを理解する上でとても大切です。
さて、このときクラスBのインスタンスを作成する方法の一つとして、クラスBの内部でクラスAのインスタンスを作成する方法があります。イメージとしては下の図のようになります。

この状態をKotlinコードで表すと次のようになります。(※各クラスのプロパティやメソッドに特に意味はありません。ここでは構造のみに注目して下さい。)
Kotlin
class ClassA {
val propA = "The property of ClassA"
}
class ClassB {
val instanceA = ClassA()
fun methodB() {
println(instanceA.propA)
}
}
クラスBの内部でクラスAのインスタンスが作成され、クラスBのメソッドの中でクラスAのプロパティにアクセスしています。この構造はシンプルで分かりやすいのがメリットですが、一方で様々なデメリットも抱えています。
例えば、クラスAに依存するクラスCが新たに追加された場合を考えてみましょう。この時、当然ですがクラスCでも改めてクラスAのインスタンスを作成しなければならなくなります。イメージ図とコード例は下のようになります。

Kotlin
class ClassA {
val propA = "The property of ClassA"
}
class ClassB {
val instanceA = ClassA()
fun methodB() {
println(instanceA.propA)
}
}
class ClassC {
val instanceA = ClassA()
fun methodC() {
println(instanceA.propA)
}
}
この例のように、いちいち各クラスの内部で個別にクラスAのインスタンスを作成しなければならないのは、ちょっと無駄が多くて手間に思えてきますよね。
ではさらに、クラスAのインスタンスを作成するのに、コンストラクタに値を渡さなくてはならなくなったらどうでしょう?例えば、IDとして整数値を渡す必要が出てきた場合を考えてみましょう。
コードは次のようになります。
Kotlin
class ClassA (private val id: Int) {
val propA = "ID:$id The property of ClassA"
}
class ClassB {
val instanceA = ClassA(10)
fun methodB() {
println(instanceA.propA)
}
}
class ClassC {
val instanceA = ClassA(10)
fun methodC() {
println(instanceA.propA)
}
}
クラスAのインスタンスを作成するのに必要な手順が追加されたことで、各クラスの内部でクラスAのインスタンスを作成するのが余計に面倒になってきました。
クラスAのコンストラクタに渡す必要のある値が増えれば増えるほど、クラスAに依存するクラスBやクラスCのコードも複雑になってしまいます。そうなるとミスが起きやすくなりますし、テストもやりづらくなってしまいます。
このように、依存関係を持つクラス間には様々な問題が発生してしまいがちです。そしてそれはプロジェクトの規模が大きくなればなるほど、無視できないレベルになります。
ではどうすればこの問題を解決できるのでしょうか?その答えの一つが、次のステップで説明する『DI(依存性注入)』です。
- POINT!
- ・あるクラスのインスタンスを作成するのに別のクラスのインスタンスが必要であるような場合に、『依存性がある(依存関係を持つ)』と言える!
- ・依存しているクラスの構造が複雑になると、それに伴って依存元のクラスも複雑になってしまう!
- ・このような依存関係の問題を解決するためのテクニックがDI(依存性注入)!