Genta Hirauchi

公開日:2020/04/22
更新日:2020/08/03

【Kotlin MVP】MVPアーキテクチャで、TODOアプリを作成する #4

アプリ開発におけるアーキテクチャ(設計)の1つに、MVPというものがあります。

本記事は、ToDoアプリの作成を通して、MVPについて学習するシリーズの第4弾です。

第4弾の今回は、Presenterを実装します。

なお、本シリーズで実装するTODOアプリのソースコードは、todo-sample | GitHubにて公開しております。

目次

Presenterの実装

interfaceの実装

Presenterを実装するにあたり、必要な処理を明確にするため、interfaceを作成します。

  • TaskContract.kt
interface TaskContract {

    interface View {

        fun onLoadTasks(tasks: List<Task>)
    }

    interface Presenter {

        fun loadTasks()

        fun insertTask(description: String)

        fun updateTaskState(task: Task)

        fun updateTaskDescription(task: Task, description: String)

        fun deleteTask(task: Task)
    }
}

TaskContractというinterfaceを作成し、さらにその中に、ViewPresenterというinterfaceを作成しました。

Viewは、Presenterを利用する側のクラスでimplementされることを想定したinterfaceです。

onLoadTasks()は、Presenterでタスクの一覧取得が行われた後に呼ばれることを想定しており、Viewをimplementしたクラスでは、onLoadTasks()をOverrideし、リスト表示の更新処理を行うようにします。

Presenterは、後に作成するTaskPresenterというクラスでimplementされることを想定したinterfaceです。

loadTasks()は、タスクの一覧取得処理が実装されることを想定しております。

insertTask()は、タスクの追加処理が実装されることを想定しております。

updateTaskState()は、タスクの状態の更新処理が実装されることを想定しております。

updateTaskDescription()は、タスクの内容の更新処理が実装されることを想定しております。

deleteTask()は、タスクの削除処理が実装されることを想定しております。

interfaceは無くても実装できますが、interfaceを実装することで、各クラスに必要な機能を整理することができ、implementしたクラスで、interfaceのメソッドの実装を強制させることができます。

Presenterの実装

続いて、TaskPresenterというMVPのPresenterに該当するクラスを作成します。

  • TaskPresenter.kt
class TaskPresenter(private val taskDao: TaskDao, private val view: TaskContract.View): TaskContract.Presenter {

    override fun loadTasks() {
        val tasks = taskDao.getAll()
        view.onLoadTasks(tasks)
    }

    override fun insertTask(description: String) {
        taskDao.insert(Task(0, 0, description))
        loadTasks()
    }

    override fun updateTaskState(task: Task) {
        task.state++
        if (task.state > 2) task.state = 0
        taskDao.update(task)
        loadTasks()
    }

    override fun updateTaskDescription(task: Task, description: String) {
        task.description = description
        taskDao.update(task)
        loadTasks()
    }

    override fun deleteTask(task: Task) {
        taskDao.delete(task)
        loadTasks()
    }
}

TaskPresenterは、先ほど実装した、TaskContractのPresenterをimplementしております。ですので、Presenterに実装されているメソッドを、Overrideしております。

コンストラクタで、前回の記事で実装したTaskDaoと、TaskContractのViewをimplementしたクラスを受け取るよう実装されております。

各メソッドでは、TaskDaoから、データベースへアクセスする処理が実装されております。

また、追加、更新、削除の処理が実行された後は、loadTasks()を呼びだし、タスクの一覧取得を行うよう実装しております。loadTasks()では、タスクの一覧を取得した後、結果をTaskContract.ViewのonLoadTasks()に渡すよう実装されております。

なお、現段階では、TaskDao経由でデータベースへのアクセスを行なっておりますが、将来的には、Repository経由でアクセスするよう変更する予定です。

Presenterを呼びだす処理の実装

最後に、MainFragmentに実装されているデータベースへのアクセス処理を削除し、Presenter経由でデータベースにアクセスするよう修正します。

  • MainFragment.kt
class MainFragment : Fragment(), TaskContract.View {

    override fun onLoadTasks(tasks: List<Task>) {
        adapter.tasks = tasks
    }

    private lateinit var adapter: TasksAdapter
    private lateinit var presenter: TaskPresenter

    (省略)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        (省略)

        val db = TaskDatabase.getInstance(context)
        presenter = TaskPresenter(db.taskDao(), this)
        presenter.loadTasks()

        addTaskButton.setOnClickListener {
            presenter.insertTask("New Task")
        }
    }

    val itemListener = object : TaskItemListener {
        override fun onStateClick(task: Task) {
            presenter.updateTaskState(task)
        }

        override fun onDescriptionClick(task: Task) {
            presenter.updateTaskDescription(task, "Updated Task")
        }

        override fun onDeleteClick(task: Task) {
            presenter.deleteTask(task)
        }
    }

    (省略)
}

MainFragmentは、TaskContractのViewをimplementし、onLoadTasks()をOverrideしております。onLoadTasks()には、タスクの一覧取得の結果が渡ってくるので、リストの更新処理を実装しておきます。

onViewCreated()では、TaskPreseterの生成と、タスクの一覧取得を行うloadTasks()の呼び出しを行なっております。

追加ボタンタップ時に呼ばれるaddTaskButton.setOnClickListenerでは、TaskPreseterのinsertTask()を呼び出し、タスクの追加処理を行うよう実装しました。

タスクの状態表示部分タップ時に呼ばれるonStateClick()では、TaskPreseterのupdateTaskState()を呼び出し、タスクの状態更新処理を行うよう実装しました。

タスクの内容表示部分タップ時に呼ばれるonDescriptionClick()では、TaskPreseterのupdateTaskDescription()を呼び出し、タスクの内容更新処理を行うよう実装しました。

タスクの削除ボタンタップ時に呼ばれるonDeleteClick()では、TaskPreseterのdeleteTask()を呼び出し、タスクの削除処理を行うよう実装しました。

実行結果

この章の修正内容はPresenterの実装 | GitHubからも確認頂けます。本記事には、重要箇所のコードのみを抜粋して掲載しておりますので、その他の修正内容に関しましては、こちらからご確認いただけると幸いです。

今回の実装はここまでとなります。
次回は、Repositoryの実装していく予定です。
ありがとうございました。