適切なiOSアーキテクチャの選択方法(パート2)

MVC、MVP、MVVM、VIPER、またはVIP

ここでパート1を参照できます。

主なiOSアーキテクチャ

簡単な概要。

MVC

MVCレイヤーは次のとおりです。

M:ビジネスロジック、ネットワーク層、データアクセス層

V:UIレイヤー(UIKitのもの、ストーリーボード、Xib)

C:モデルとビューの間の調停を調整します。

MVCを理解するには、それが発明されたコンテキストを理解する必要があります。 MVCは、ビューが状態を持たない古いWeb開発の時代に発明されました。昔は、Webサイトの視覚的な変更が必要になるたびに、ブラウザーはHTML全体を再度リロードします。当時、ビューステートが維持および保存されるという概念はありませんでした。

たとえば、同じHTMLファイル、PHP、およびデータベースアクセス内で混在する開発者がいました。そのため、MVCの主な動機は、ビューレイヤーをモデルレイヤーから分離することでした。これにより、モデルレイヤーのテスト容易性が向上しました。 MVCの場合、View and Modelレイヤーはお互いについて何も知らないはずです。これを可能にするために、Controllerという名前の中間層が発明されました。これが適用されたSRPでした。

MVCサイクルの例:

  1. ビューレイヤーのユーザーアクション/イベント(例:更新アクション)が起動され、そのアクションがコントローラーに伝達されます。
  2. モデルレイヤーにデータを要求するコントローラー
  3. コントローラーへの返品データのモデル化
  4. コントローラーは、ビューが新しいデータで状態を更新するように言います
  5. 状態を更新して表示

Apple MVC

iOSでは、View ControllerはUIKitおよびライフサイクルビューに結合されているため、純粋なMVCではありません。ただし、MVCの定義では、コントローラーがビューまたはモデル固有の実装を認識できないと言うことはありません。彼の主な目的は、モデルレイヤーとビューレイヤーの責任を分離して、モデルレイヤーを単独で再利用してテストできるようにすることです。

ViewControllerにはビューが含まれ、モデルを所有しています。問題は、ViewControllerでコントローラーコードとビューコードを記述するために使用したことです。

MVCは、Massive View Controllerと呼ばれる問題をしばしば作成しますが、それは、十分な複雑さを備えたアプリでのみ発生し、深刻な問題になります。

開発者がView Controllerを管理しやすくするために使用できる方法がいくつかあります。いくつかの例:

  • テーブルビューメソッドデータソースなどの他のクラスのVCロジックを抽出し、デリゲートデザインパターンを使用して他のファイルのデリゲートを行います。
  • 構成を使用して、より明確な責任の分離を作成します(たとえば、VCを子View Controllerに分割します)。
  • コーディネーターのデザインパターンを使用して、VCにナビゲーションロジックを実装する責任を取り除きます。
  • ロジックをカプセル化し、データモデルをエンドユーザーに提示されるデータを表すデータ出力に変換するDataPresenterラッパークラスを使用します。

MVC vs MVP

MVPの図を見る方法はMVCに非常に似ています

MVCは一歩前進しましたが、それでもいくつかのことに対する不在または沈黙が特徴でした。

一方、World Wide Webは成長し、開発者コミュニティの多くのことが進化しました。たとえば、プログラマーはAjaxの使用を開始し、HTMLページ全体ではなく、ページの一部のみを一度にロードしました。

MVCでは、コントローラーがViewの特定の実装(不在)を知らないことを示すものは何もないと思います。

HTMLはViewレイヤーの一部であり、多くの場合、愚かでした。場合によっては、ユーザーからのイベントのみを受け取り、GUIの視覚的コンテンツを表示します。

Webページの一部がパーツに読み込まれ始めたため、このセグメンテーションはビュー状態を維持する方向に進み、プレゼンテーションロジックの責任分離の必要性が高まりました。

プレゼンテーションロジックは、UIの表示方法とUI要素の相互作用を制御するロジックです。例は、ロードインジケータが表示/アニメーションを開始するタイミングと表示/アニメーションを停止するタイミングの制御ロジックです。

MVPおよびMVVMでは、ビューレイヤーは、ロジックやインテリジェンスを持たない、愚かなものである必要があり、iOSでは、ビューコントローラーはビューレイヤーの一部である必要があります。 Viewが愚かであるという事実は、プレゼンテーションロジックでさえもViewレイヤーの外に留まることを意味します。

MVCの問題の1つは、プレゼンテーションロジックがどこに留まるかが明確でないことです。彼は単にそれについて沈黙しています。プレゼンテーションロジックは、ビューレイヤーまたはモデルレイヤーのどちらに配置する必要がありますか?

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

次の例を考えてみましょう。名と姓を持つユーザーがいます。ビューでは、ユーザー名を「姓、名」(「Flores、Tiago」など)として表示する必要があります。

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

let firstName = userModel.getFirstName()
let lastName = userModel.getLastName()
nameLabel.text = lastName +“、“ + firstName

つまり、UIロジックを処理するのはビューの責任であることを意味します。ただし、これによりUIロジックの単体テストが不可能になります。

もう1つのアプローチは、表示する必要があるデータのみをモデルに公開し、ビューからビジネスロジックを非表示にすることです。しかし、その後、ビジネスロジックとUIロジックの両方を処理するモデルになります。単体テスト可能ですが、モデルは最終的に暗黙的にビューに依存します。

let name = userModel.getDisplayName()
nameLabel.text = name

MVPはそれについて明確であり、プレゼンテーションロジックはプレゼンターレイヤーにとどまります。これにより、Presenterレイヤーのテスト容易性が向上します。モデルとプレゼンターレイヤーは簡単にテストできます。

通常、MVPの実装では、ビューはインターフェイス/プロトコルの背後に隠されており、プレゼンターではUIKitへの参照はありません。

留意すべきもう1つのことは、推移的な依存関係です。

コントローラーに依存関係としてビジネスレイヤーがあり、ビジネスレイヤーに依存関係としてデータアクセスレイヤーがある場合、コントローラーにはデータアクセスレイヤーに対する推移的な依存関係があります。 MVP実装は通常、すべてのレイヤー間でコントラクト(プロトコル)を使用するため、推移的な依存関係はありません。

また、さまざまなレイヤーがさまざまな理由で、さまざまなレートで変化します。そのため、レイヤーを変更するときに、他のレイヤーで二次的な影響や問題が発生することは望ましくありません。

プロトコルはクラスよりも安定しています。プロトコルには実装の詳細と契約がありません。そのため、他のレイヤーに影響を与えることなく、レイヤーの実装の詳細を変更することができます。

そのため、コントラクト(プロトコル)はレイヤー間のデカップリングを作成します。

MVP対MVVM

MVVM図

MVPとMVVMの主な違いの1つは、MVPではプレゼンターがインターフェイスを介してビューと通信し、MVVMではビューがデータとイベントの変更に対応していることです。

MVPでは、インターフェイス/プロトコルを使用して、プレゼンターとビューを手動でバインドします。
MVVMでは、RxSwift、KVOなどを使用して自動データバインディングを作成するか、ジェネリックとクロージャーを使用したメカニズムを使用します。

MVVMでは、ViewModelとViewの間にコントラクト(例:Javaインターフェイス/ iOSプロトコル)さえ必要ありません。これは、通常、Observer Design Patternを介して通信するためです。

MVPは、プレゼンターレイヤーがビューレイヤーに注文を委任するため、デリゲートパターンを使用します。したがって、インターフェース/プロトコル署名のみであっても、ビューについて何かを知る必要があります。通知センターとTableViewデリゲートの違いを考えてください。通知センターには、通信チャネルを作成するためのインターフェイスは必要ありませんが、TableView Delegatesは、クラスが実装する必要があるプロトコルを使用します。

ローディングインジケータのプレゼンテーションロジックを考えてください。 MVPでは、プレゼンターはViewProtocol.showLoadingIndicatorを実行します。 MVVMでは、ViewModelにisLoadingプロパティがある場合があります。自動データバインディングを介したビューレイヤーは、このプロパティが変更され、それ自体が更新されることを検出します。プレゼンターが注文を出すため、MVPはMVVMよりも必須です。

MVVMは直接的な注文よりもデータの変更に関するものであり、データの変更とビューの更新を関連付けます。 RxSwiftと関数型リアクティブプログラミングパラダイムをMVVMと共に使用すると、コードの命令性が低下し、宣言性が高まります。

MVVMは、分離された方法でコンポーネント間でデータを転送するObserver Design Patternを使用するため、MVPよりもテストが簡単です。
したがって、ViewとPresenter間の通信をテストするためにメソッド呼び出しをモックするのではなく、2つのオブジェクトを比較するだけでデータの変化を調べるだけでテストできます。

PS:この記事を更新して大きく成長させたので、3つの部分に分ける必要がありました。ここでパート3を読むことができます。

パート2はここで終わりです。すべてのフィードバックを歓迎します。パート3では、VIPER、VIP、リアクティブプログラミング、トレードオフ、制約、および文脈感覚について説明します。

読んでくれてありがとう!この記事が気に入ったら、拍手してください
他の人も読むことができるように:)