日々是好日

プログラミングについてのあれこれ、ムダ知識など

MVVM 完全に理解した - 11

前回は、 Android Architecture BluePrints の TasksRepository クラスについて見てきましたが、途中で Dependency Injection (長いので以下 DI )に関係してそうな Injection というクラスが出てきました。

今回はこの Injection クラスより、どのようにデータベース操作を実装しているのかを見ていきます。前回よりは短いはず←

最悪まとめさえ読んでいただければ……

Injection が出てきたところ(復習)

Injection クラスは ViewFactory のインスタンス化のところで出てきました。
TasksRepository を生成するために使用しています。

private var INSTANCE: ViewModelFactory? = null

fun getInstance(application: Application) =
        INSTANCE ?: synchronized(ViewModelFactory::class.java) {
            INSTANCE ?: ViewModelFactory(application,
                    //↓ココ!
                    Injection.provideTasksRepository(application.applicationContext))
                    .also { INSTANCE = it }
        }

ではこのInjection.provideTasksRepositoryの中身を見てみます。

Injection の実装

object Injection {
    fun provideTasksRepository(context: Context): TasksRepository {
        val database = ToDoDatabase.getInstance(context)
        return TasksRepository.getInstance(FakeTasksRemoteDataSource,
                TasksLocalDataSource.getInstance(AppExecutors(), database.taskDao()))
    }
}

(文字大杉)

val databaseという変数から、データベースオブジェクトを生成しているようです。ここはまあいいでしょう( Room を用いて DB を構築している様子)。

次の行で、TasksRepository.getInstanceで TasksRepository インスタンスを生成しています。
TasksRepository は ViewModel や他のクラスとデータベースとの仲介を行うクラスでした。TasksLocalDataSource というクラス名からして、ローカルのデータソースを操作するようです。
なおあまり関係ないので端折りますが、AppExecutors は非同期処理を行うためのクラス、リモートリポジトリはモックオブジェクトを入れているようです。

TasksLocalDataSource の実装

では TasksLocalDataSource クラスを見てみます。

class TasksLocalDataSource private constructor(
        val appExecutors: AppExecutors,
        val tasksDao: TasksDao
) : TasksDataSource

TasksDataSource インターフェースを実装しています。前回少しだけ書いたように、このインターフェースは次のメソッドを実装します。

//再掲
interface TasksDataSource {
    fun getTasks
    fun saveTask
    fun completeTask ... //などなど
}

ではでは、例としてcompleteTaskの実装を見てみます。

override fun completeTask(task: Task) {
    appExecutors.diskIO.execute { tasksDao.updateCompleted(task.id, true) }
}

TasksLocalDataSource クラスからはデータベース操作のクエリ等を発行せず、taskDao.updateCompletedtaskDaoに処理を委譲していました。

図にするとやり取りはこんな感じになると思います。taskDaoは、雑ですがローカル(リモート)なデータにあたると考えてください。

f:id:kcpoipoi:20181217223937p:plain

TaskDao クラスではクエリを発行しており、真のデータベースとのやり取りはこのクラスで行っていました。

まとめ

というわけで、あまり書き散らかしたくないデータベース(ローカル)とのやり取りは、TaskDao というところで行っていました。TasksRepository や TasksLocalDataSource はクエリを意識しないでデータベースを操作するためのラッパーという感じですかね。

なお地味にスルー(というか書けなかった)してますが、TasksRepository のコンストラクタに各 TasksXXXDataSource オブジェクトが外部から渡されています。

普通に考えたら、TasksRepository の中で TasksLocalRepository のインスタンスを生成すればいい話です。

しかしテストを考えると、外部から TasksLocalRepository のモックオブジェクトを渡してやれば、テストが容易にできるようになります。

TasksRepository の中ではインスタンスの実体がなんであるかは関係ありません。ただ TasksDataSource インターフェースを実装してさえすればOKです。(MockRepository : TasksDataSource

これが依存性の注入、Dependency Injection です。

……いやお前本当に理解してんのかってマサカリ飛んできそうですが、すみません実際にDIでテストとか行ったことはないのであまりつよいマサカリは投げないでください(早口)←

所感

いやーすごい。深い。深すぎて追いきれない(´・ω・`)

あと残すところ3回ですが、なんと私生活でちょっとした動きがあったので、更新間隔空くと思います。
ミニアプリの構想も少しできてきた(今日考えた)ので、まあ14回目には何かしら作れればなーと思います。