Androidのアーキテクチャパターン その1: Model-View-Controller

Florina Muntenescu
Florina Muntenescu

Follow

Nov 1, 2016 – 6 min read

1 年前、現在の Android チームの大半が upday で働き始めたとき、アプリケーションは私たちが望んでいた堅牢で安定したアプリとはほど遠い状態でした。 UI の継続的な変更と、私たちが必要とする柔軟性をサポートするアーキテクチャの欠如です。 このアプリは、6ヶ月間ですでに4回目の再設計が行われていました。 Model-View-Controller パターンが何であるか、それが長年にわたって Android でどのように適用されてきたか、テスト容易性を最大化するためにどのように適用すべきか、そして、その利点と欠点について一緒に考えてみましょう。

ユーザー インターフェイス ロジックがビジネス ロジックよりも頻繁に変更される傾向がある世界では、デスクトップおよび Web 開発者はユーザー インターフェイス機能を分離する方法を必要としていました。

  • モデル – データ層で、ビジネス ロジックの管理とネットワークまたはデータベース API の処理を担当します。
  • ビュー – UI 層で、モデルからのデータを視覚化します。

Model-View-Controller クラス構造

つまり、コントローラとビューは両方ともモデルに依存しています:データを更新するコントローラとデータを得るためのビューです。 しかし、当時のデスクトップやWebの開発者にとって最も重要なことは、モデルが分離されており、UIから独立してテストできることです。 MVCのいくつかのバリエーションが登場しました。 最もよく知られているのは、モデルが受動的か能動的に変更されたことを通知するかに関連するものです。 以下はその詳細です。

Passive Model

Passive Model バージョンでは、Controller が Model を操作する唯一のクラスである。 ユーザーのアクションに基づいて、ControllerはModelを変更する必要があります。 モデルが更新された後、Controller はビューにも更新が必要であることを通知します。 その時点で、View はモデルからデータを要求することになります。

Model-View-Controller – passive Model – behavior

Active Model

Model は、コントローラがモデルを変更する唯一のクラスではない場合について、更新について View と他のクラスに知らせる方法が必要である。 これは Observer パターンの助けを借りて実現されます。 モデルは更新に関心を持つオブザーバのコレクションを含んでいます。

Model-View-Controller – active Model – class structure

モデルが更新するたびに、それはまたオブザーバーのコレクションを繰り返し、updateメソッドを呼び出すでしょう。 ビューのこのメソッドの実装は、モデルからの最新データの要求をトリガーします。

Model-View-Controller – active Model – behavior

Model-View-Controller in Android

Android がますます普及し始めた2011年頃に、アーキテクチャについての質問が自然に出てきたものであった。 MVC は当時最も人気のある UI パターンの 1 つだったので、開発者は Android にも適用しようとしました。

StackOverflow で「How to apply MVC in Android」といった質問を検索すると、最も人気のある回答の 1 つに、Android ではアクティビティはビューとコントローラの両方であるとありました。 今思えば、これはほとんどクレイジーに聞こえますね。 しかし、その時点では、モデルをテスト可能にすることに主眼が置かれており、通常、ビューとコントローラーの実装の選択はプラットフォームに依存していました。

How Should MVC Be Applied in Android

Nowadays, the question of how to apply the MVC patterns has a answer that found easy to the other. アクティビティ、フラグメント、ビューは、MVCの世界ではビューであるべきです。 コントローラは、Android のクラスを拡張したり使用したりしない別のクラスであるべきで、モデルも同じです。

コントローラとビューを接続するときに、コントローラがビューに更新を指示する必要があるので、1 つの問題が発生します。 受動的なモデル MVC アーキテクチャでは、Controller は View への参照を保持する必要があります。 テストに焦点を当てながら、これを行う最も簡単な方法は、アクティビティ/フラグメント/ビューが拡張するBaseViewインターフェイスを持つことです。

Advantages

Model-View-Controller パターンは、関心の分離を高度にサポートします。 この利点はコードのテスト容易性を高めるだけでなく、拡張を容易にし、新しい機能のかなり容易な実装を可能にします。

モデル クラスは Android クラスへの参照を持たないので、ユニット テストが容易です。 Controller は Android のクラスを拡張したり実装したりせず、View のインターフェイス クラスへの参照を持つ必要があります。

View が単一責任の原則を尊重する場合、その役割はユーザー イベントごとにコントローラーを更新し、ビジネス ロジックを実装せずにモデルからデータを表示するだけです。 この場合、UI テストはビューの機能をカバーするのに十分であるべきです。

The View Depends On The Controller And On The Model

View のモデルへの依存は、複雑な View において欠点となり始めます。 ビューのロジックを最小限にするために、モデルは、表示されるすべての要素に対してテスト可能なメソッドを提供できるようにする必要があります。 アクティブなモデルの実装では、データのすべてのタイプのオブザーバーが必要であることを考えると、これはクラスとメソッドの数を指数関数的に増やします。

View がコントローラーとモデルの両方に依存することを考えると、UI ロジックの変更にはいくつかのクラスの更新が必要で、パターンの柔軟性を低下させる可能性があります。

Who handles the UI Logic?

MVC パターンによると、コントローラーはモデルを更新し、ビューはモデルから表示されるデータを取得します。 しかし、データをどのように表示するかは誰が決定するのでしょうか。 モデルでしょうか、それともビューでしょうか。 次のような例を考えてみましょう。Userという名前と姓のデータがあります。 ビューでは、ユーザー名を “Lastname, Firstname” として表示する必要があります (例: “Doe, John”).

モデルの役割が「生の」データを提供するだけである場合、ビューのコードは次のようになります。

String firstName = userModel.getFirstName(); 
String lastName = userModel.getLastName(); nameTextView.setText(lastName + ", " + firstName)

つまり、これはビューの責任としてUI論理を処理することを意味します。 しかし、これでは UI ロジックをユニット テストすることができません。

もう 1 つの方法は、モデルが表示する必要のあるデータのみを公開し、ビジネス ロジックをビューから隠すというものです。 しかし、その場合、ビジネスと UI ロジックの両方を処理するモデルで終わります。

String name = userModel.getDisplayName(); nameTextView.setText(name);

Conclusion

Android の初期には、モデル-ビュー-コントローラ パターンは多くの開発者を混乱させ、ユニット テストが不可能ではないにしても、難しいコードにつながったと思われました。

モデルからビューに依存し、ビューにロジックを持つことは、アプリを完全にリファクタリングしない限り、コード ベースを回復できない状態に追い込みました。 アーキテクチャにおける新しいアプローチとは何だったのでしょうか、そしてそれはなぜだったのでしょうか。 このブログの記事を読んで、その理由を知ってください。