手動と自動DIの違い

#DIとは？
手動でDIしている例もあるので以下記事を参照ください。


https://t.co/Sn1JldWAwG?amp=1

また、筆者Androiderなのでコード例がKotlinなのですがご了承ください。

#手動の良くないポイント
DIは、

「手動でも簡単に実装出来てぶっちゃけライブラリの力とか借りなくても大丈夫じゃね？」

とか思うのですが、やはり物事には必ずデメリットがつきものです。

ドキュメント先輩によると依存関係やクラスが複雑になった時に大変とあります。
まぁ、なんか依存関係がA,B,Cとは比べ物にならないくらい複雑になると大変なのかなぁという感じです。

#手動の良くないポイントを具体例で
具体例をもとに考えると分かりやすいかもしれないです！
MVVMにて、Repository層ではリモートとローカルのデータアクセスをDIするかと思います。

※Androidのドキュメントからコードと解説をお借りしいます。

```kotlin
 class LoginActivity: Activity() {

        private lateinit var loginViewModel: LoginViewModel

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)

            // In order to satisfy the dependencies of LoginViewModel, you have to also
            // satisfy the dependencies of all of its dependencies recursively.
            // First, create retrofit which is the dependency of UserRemoteDataSource
            val retrofit = Retrofit.Builder()
                .baseUrl("https://example.com")
                .build()
                .create(LoginService::class.java)

            // Then, satisfy the dependencies of UserRepository
            val remoteDataSource = UserRemoteDataSource(retrofit)
            val localDataSource = UserLocalDataSource()

            // Now you can create an instance of UserRepository that LoginViewModel needs
            val userRepository = UserRepository(localDataSource, remoteDataSource)

            // Lastly, create an instance of LoginViewModel with userRepository
            loginViewModel = LoginViewModel(userRepository)
        }
    }
```
恐らくLoginViewModelは、下記のようなクラスでしょう。

```kotlin:LoginViewModel.kt
class LoginViewModel(
     private val repository: Repository
) : ViewModel() {
       // メンバ達・・・
}
```

Activityとの依存関係の部分で、ViewModelのインスタンスを生成する必要がありますが、ボイラープレートコードがかなり目立ちますね。

他でもう一度LoginViewModelのインスタンスを生成しようものならまた同じ処理を書かなくてはなりません。

また、LoginViewModelを呼ぶには、Repository呼ぶ必要があり、Repositoryを呼ぶには、LocalDataSourceとRemoteDetaSourceを呼ばなくてはなりません。

つまり、
LocalDataSource,RemoteDataSource -> Repository -> LoginViewModel
の順に生成しないといけないので、これを書いたり後の人が読ませるのはナンセンスですよね。

それと、このように個別にインスタンスを作ってしまうと代替クラスを作れないテスタビリティが低下するのとインスタンスが無駄に生成されてしまいます。

無駄な生成はシングルトンで解消できますが、テストが余計に大変になりますね。

#解決策
解決策は、コンテナという役割を追加してクラス内で共通のインスタンスを提供することです。

```kotlin
    // Container of objects shared across the whole app
    class AppContainer {

        // Since you want to expose userRepository out of the container, you need to satisfy
        // its dependencies as you did before
        private val retrofit = Retrofit.Builder()
                                .baseUrl("https://example.com")
                                .build()
                                .create(LoginService::class.java)

        private val remoteDataSource = UserRemoteDataSource(retrofit)
        private val localDataSource = UserLocalDataSource()

        // userRepository is not private; it'll be exposed
        val userRepository = UserRepository(localDataSource, remoteDataSource)
    }
```
このようにRepositoryクラスのインスタンスをここで作っておき、

```kotlin
    // Custom Application class that needs to be specified
    // in the AndroidManifest.xml file
    class MyApplication : Application() {

        // Instance of AppContainer that will be used by all the Activities of the app
        val appContainer = AppContainer()
    }
    
```
アプリ起動時に発火されるApplicationクラスで、呼び出しておきます。
こうすれば、下記の様にViewModelクラスを一個だけ生成し、再利用することに成功しています。

```kotlin
    class LoginActivity: Activity() {

        private lateinit var loginViewModel: LoginViewModel

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)

            // Gets userRepository from the instance of AppContainer in Application
            val appContainer = (application as MyApplication).appContainer
            loginViewModel = LoginViewModel(appContainer.userRepository)
        }
    }
```

もう完全にAndroiderに向けての記事になってしまいますが、コンテナをアプリ内で共通化する最後の処理はこちら

```kotlin
    class LoginActivity: Activity() {

        private lateinit var loginViewModel: LoginViewModel

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)

            // Gets userRepository from the instance of AppContainer in Application
            val appContainer = (application as MyApplication).appContainer
            loginViewModel = LoginViewModel(appContainer.userRepository)
        }
    }
```

#終わりに
とはいえ、まだボイラープレートが多いですし、ライフサイクルの管理は手動でしなくてはならないので問題ありです。

なんか、手動と自動の違いを解説するつもりでしたが、結局DIを効率化しようとしたら新たに自動化すべきポイントが出てきて真の自動化などない思いました（完）


# 参考

https://developer.android.com/training/dependency-injection/manual?hl=ja