【Android】分かった気になれる!アーキテクチャ・MVVM概説
先日、Google推奨アーキテクチャであるMVVMの概念を理解するため
Google公式アーキテクチャガイドを読んだので記事にしてみます。
以下のような疑問をお持ちの方が
これらをなんとなく理解できるような記事を書ければと思います。
- MVVMとは?
- MVVMを導入する理由は?
- そもそも、アーキテクチャとは?
- そもそも、アーキテクチャを導入する理由は?
- etc.
アーキテクチャの概念を簡単に知りたい方に気軽に読んでいただければ嬉しいです!
Kotlinを用いたAndroidアプリ開発ではMVVMが推奨されていますが、それ以外のiOSアプリやWebアプリを開発している方のためにもなると思います。
アーキテクチャとは
アーキテクチャを直訳すると「建築」や「構造」などの意味があります。
アプリ開発においてはアプリの設計手法のことを表し、
MVC/ MVP/ MVVMなど、いくつかのパターンがあります。
これらは先人のエンジニアがよりよいアプリ設計を目指して確立されてきたものであり、
アーキテクチャを適切に導入することで
洗練されたアプリを開発することができるようになります。
そのようにしてできたアプリは
開発者にとって、またアプリを使用するユーザーにとっても
メリットとなる点が多くあります。
そのメリットについては後述します。
アーキテクチャの基本概念
アーキテクチャの基本概念・ルールは大きく分けて2つあります。
- 関心の分離:役割分担
- UIをモデルで操作する
1. 関心の分離:役割分担
主にデータの管理(データの取得・保存)に関して、
各クラスの関心を分離させるべきと言う考え方です。
各クラスの役割を定め、自らの役割を全うさせることで
効率的で、洗練されたアプリを作成することができます。
逆に言うと、データの管理に関して役割分担を行わないと
主にLifecycleに関して様々な問題が発生します。
例えば、もしActivityやFragmentにデータの取得・保存・UIの表示など
全ての処理を記述するとどうなるでしょうか?
まず、他人(未来の自分を含む)が理解しづらくなり、修正も困難になるでしょう。
また、ひとつの問題としてメモリ不足になることが挙げられます。
ActivityやFragmentは画面を回転したり、アプリをバックグラウンドに移したりするだけでLifecycleが終了します。
つまり、ActivityやFragmentを再度表示する際には
もう一度全ての処理を実行しなければならなくなります。
そのような高負荷な処理を連続で行うことにより、メモリ不足になることは十分に考えられます。
2. UIをモデルで操作する
上記の1.の例で示したように、
ActivityやFragmentにデータ管理に関する全ての処理を記述すると
様々なLifecycleに関する問題が発生することは想像できたでしょうか。
その問題の解決法として、UIをモデルで操作すること、
つまり、Lifecycleとは無関係な永続化モデルとしてデータを保存することが推奨されています。
メリット
アーキテクチャを導入することによるメリットを
開発者視点・ユーザー視点の双方から考察してみます。
開発者視点でのメリット
クラスの役割が明確であるため、
- データの管理が楽になる
- テストをしやすくなる
- データの参照先を変更するときなど、コードを書き換える際の工数が大幅に削減される
- その際のケアレスミスが削減される
ユーザー視点でのメリット
データを永続モデルとして保存することで、
- アプリのUIをより早く表示させることができる
- メモリ不足でアプリが落ちる可能性が低くなる
MVVMとは
前置きが長くなりましたが、本題であるMVVMの概要を説明したいと思います。
MVVM とは、Model - View - View Model の略称で
アーキテクチャのパターンの一つです。
Googleは基本的なアーキテクチャとしてMVVMを推奨しており
そのためのコンポーネント(部品)やライブラリを提供しています。
参照:アーキテクチャ コンポーネント
Googleによると、MVVMは以下のような図で表されます。
補足:
- View:Activity/ Fragment
- ViewModel
- (Repository)
- Model
- (Remote Data Source)
参照:https://developer.android.com/jetpack/docs/guide#overview
以下では、これら各コンポーネントの
- 役割
- 関係性
を記述していきたいと思います。
各コンポーネントの関係性についての基本ルール
各コンポーネントは矢印の方向にしか参照できません。
つまり、矢印が指す先のコンポーネントのメソッドを利用したりすることでしか
データを取得することができません。
View
Viewとは、ActivityやFragmentなど、UIを表示するためのコンポーネントです。
1. 役割
-
UIを表示・更新すること
例)ユーザー情報を取得し、ユーザー名・ポイント数などを表示する
また、ViewModelにてLiveData<User>で定義されたユーザー情報が更新されたことを
observe(観察)することで、随時UIの更新を行う -
ユーザー操作を検知すること
例)Buttonの押下を検知し、画面遷移を行う
2. ViewModelとの関係性
- UIを表示するために必要なデータの取得処理やユーザー操作によるデータの更新処理をViewModelに委任する
例)ViewModelで定義されたユーザー情報取得メソッドを呼び出す
ViewModel
1. 役割
-
ViewがUIを表示するために必要なデータを取得し、Viewが使いやすい形で準備する
例)Repository からユーザー情報(Userクラス)を取得し、ユーザー名((User).name)のみをViewに渡す -
Viewから送られてきた値を保存するためのメソッドを呼ぶ ー2020/05/15 追加
例)入力されたテキスト(inputText)を基にUserクラスのインスタンスを作成しそれを保存する。
save(user = User(name: inputText)
2. Repositoryとの関係性
-
モデルクラスに落とし込んで整えられたデータの取得/保存を、Repositoryに委任する
例)Repositoryのユーザー情報(Userクラス)取得/保存メソッドを呼ぶ -
Repositoryとの結びつけは、DIライブラリDagger2 などで実装される2020/05/14更新
必ずしもDIライブラリを使用する必要はない
(Repository)
RepositoryはViewModelに含まれる場合もあります。
Repositoryを設置することで、ViewModelの負担を減らすことができます。
また、Modelからのデータの取得をRepositoryに全て委任することで、
Repositoryがデータの取得先を変更したとしても、
ViewModelを変更する必要がなくなります。
1. 役割
-
データの取得方法・保存方法を把握
例)ユーザー情報の取得先は、リモートのDB。
そのデータをローカルのDBに保存。
リモートでユーザー情報が更新されれば随時読み込んでローカルのDBに保存。
ローカルのDBに対象のユーザーのユーザーアカウントが存在する場合、
リモートではなく、ローカルのDBにユーザー情報を取得しに行く。
など -
アプリ内で扱いやすいように整えられたAPIを提供
例)jsonファイルから得られたレスポンスを、Userクラス(ユーザー情報のモデルクラス)やUserクラスのコンストラクタの型に当てはめて、データを取得する
2. Modelとの関係性
- Modelからデータを取得する
例)上記参照。
Model
データを実態として保存しているコンポーネント
1. 役割
- データを永続化する
例)ユーザー情報を実態として永続的に保存する
2. DBとの関係性
- Roomライブラリでは、DAO(Data Access Object)を通して、DBにデータを挿入・取得する
例)Userクラス(ユーザー情報のモデルクラス)で実態化されたデータを、DBの仕様に合わせてDBに保存する。逆に、DBからデータを読み込みUserクラスとして実態を保持する。
まとめ
今回、本当にざっくりですがアーキテクチャ・MVVMの概要を記事にしてみました。
実装に関する内容を含めることができませんでしたが、
今後各種ライブラリの使用法を身につけていきたいと考えているため
随時記事の更新・投稿を行っていければと考えています。
学習したい具体的なライブラリ:
- Dagger2:DI
- Room:永続化
- Retrofit:通信系
- Espresso:テスト
- etc.
いくつか記事にまとめています。気になるものがあれば、ぜひこちらもご覧になってください!
- 【Android】はじめてのDataBinding:View - ViewModel 間の処理
- 【Android】はじめてのRoom:Modelに該当し、DBとのやりとりを行うを自動化
- DI(依存性の注入)とは依存性を注入するということである、、?:DIについて
- 【Android】はじめてのDagger2:DIライブラリ
その他、ご指摘箇所があればコメントいただけると嬉しいです!
Discussion