Android Advent Calendar 2016 の 5日目を担当します @kobakei です。
Androidの設計やアーキテクチャの話となると、MVC, MVP, MVVM, Reduxなど色々なパターンが試されているものの、万人が「これを使っとけば間違いない!」的なものはまだない印象です。
この記事では、どのパターンがいいか/悪いかみたいなマサカリが飛びそうな話題はせず、Android初心者が最低限これくらいはやっといたほうがいいよという設計を紹介します。
※shibuya.apk 11で話した内容をベースにまとめています。スライドはこちら。
モデルとビューを分ける
それぞれのMVX系パターンはプレゼンターやビューモデルなどの責務は異なっていますが、**「モデルとビューは別」**という点は共通しています。なので、少なくともモデルとビューは分けてしまいましょう。
Androidの場合ActivityやFragmentにビジネスロジックを書いていくとどんどん肥大化してすぐにマッチョな辛いクラスになってしまいます。ビジネスロジックはクラスを切り出して、ビューに依存しないようにしておきましょう。
なお、ビジネスロジックをActivity/Fragmentから追い出しても、Activity/Fragmentがいずれ肥大化するときは来ます。そうなったときには、MVX系パターンやReduxから自分に合ったものを試せば良いと思います。
モデルの依存関係を一方向にする
モデルを切り出していくと、あるモデルが別のモデルに依存したくなるケースが出て来るでしょう。このとき、安易に依存関係を持つと、いずれモデルが相互に依存したり、依存関係が複雑になりすぎたりして手に負えなくなります。
そんなときは、モデルの中にも階層を設けることにして、モデルが依存していいのは自分より下の層のクラスのみという制約を与えます。例えば、私の作っているサロン予約アプリでは、以下の3層になっています(注意:ここでは例として3層ですが、アプリの規模によっては2層でいいこともあるし、4層以上になるかもしれません)
- ユースケース
- ビジネスロジックを実現する層。「サインインする」「サロンを予約する」などの処理ごとに1クラス。
- リポジトリ
- エンティティのCRUDを担う層。エンティティごとに1クラス。
- インフラ
- SQLiteやREST APIなどからデータを取り出し、エンティティに変換する層。ほぼライブラリそのまま。
ここで上の制約をあてはめると、あるユースケースが依存していいのはリポジトリのみになります。他のユースケースに依存してはいけないし、リポジトリを飛び越してインフラを呼んではいけません。
このようにクラスの依存関係を一方向にすることで、コードを見ただけで理解できるように設計をシンプルに維持できます。
DI(依存性注入)を使う
あるクラスAが他のクラスBのインスタンスに依存している場合、コンストラクタで引数で渡すなどしますが、これでは後で別のクラスB'への依存に変更しようとしたときにコードを大きく書き換える必要があります。DIはこうした問題を解決する、クラス間の依存性を解決する手法です。
始めたばかりの人だと、DIの必要性について疑問を感じているかもしれません。実際、DaggerなどのDIライブラリは理解するまでに少し努力がいるように感じます。しかし、DIの導入はできるだけ最初のうちから行っておいたほうがよいです。これは、途中で導入するよりも最初に導入しておいたほうがハードルが低いのと、最初から導入しておくことでテストしやすい設計になりやすいためです。
はじめから「テストはがっつり書こう」「単体テストくらいは書こうかな」と決めているなら勿論のこと、「サービスがどれくらい続くかわからないから今はまだテスト書くか決めてないや」という場合にも、書こうと思えばいつでもテストが書ける状態にしておくことはとても大事です。
DIライブラリは、おそらくGoogleのDaggerが最もメジャーかと思われますので、とりあえずはこれを使っとけば良いと思います。