MVVMの使用を停止する方法

双頭MVVM

最近のDroidConモスクワ2016で、データバインディングライブラリを使用したMVVMについての講演と、MVPでの作業に役立つMoxyライブラリについての講演がありました。 実際、過去6か月間、ライブプロジェクトで両方のアプローチをテストすることができました。 そして、Databinding Libraryをマスターし、実稼働環境で MVVMプロジェクトをリリースしてから、このパターンを使用したくない理由を理解するまでの私の道についてお話したいと思います







Databinding Libraryに夢中になり、MVVMでアプリケーションを構築することを決めたすべての人に献身的に、あなたは勇敢な人です!


データバインディングライブラリ



Databinding Libraryを理解し始めて、感銘を受けました。 既にそれをよく知っている人は私を理解するでしょう、そして、残りのために、このライブラリでの仕事は次のようになります:













Databinding Libraryを使用すると、次のことができます。









幸運は常に複雑なトピックであったため、最後のポイントは私にとって特に喜ばしいことです。 画面に3つの状態(読み込み、データ、エラー)を表示する必要がある場合は、問題ありません。 ただし、データに応じて要素の状態にさまざまな要件がある場合(たとえば、テキストが空でない場合にのみテキストを表示したり、値に応じて色を変更したりする場合)、インターフェイス状態のすべての可能なオプションを備えた大きなスイッチ 、または多くのフラグとコードが必要になる場合があります要素に値を設定する方法で。

したがって、Databinding Libraryにより状態の操作が容易になるという事実は大きなプラスです。 たとえば、 xml android:visibility=”@{user.name != null ? View.VISIBLE : View.GONE}”



android:visibility=”@{user.name != null ? View.VISIBLE : View.GONE}”



、ユーザー名でTextViewを非表示または表示するタイミングを考えることはできなくなりました。 名前を付けるだけで、表示は自動的に変更されます。







ViewModel



しかし、 データバインディングをより積極的に使用し始めると、 xmlでより多くのコードを取得できます。 そして、 レイアウトをダンプに変えないために、このコードを取り出すクラスを作成します。 また、 xmlではプロパティ呼び出しのみが残ります。 例を挙げましょう。 Userクラスがあるとします:







 public class User { public firstname; public lastname; }
      
      





そして、UIでフルネームを確認し、 xmlで記述します。







 <TextView android:text="@{user.firstname + user.lastname}" />
      
      





私はこれをxmlで実際に見たくないので、このロジックを転送するクラスを作成します。







 public class UserViewModel extends BaseObservable { private String name; @Bindable public String getFullname() { return name; } public void setUser(User user) { name = user.firstname + user.lastname; notifyPropertyChanged(BR.name); } }
      
      





ライブラリの作成者は、そのようなクラスをViewModelと呼ぶことをお勧めします(驚くほど、MVVMパターンのように)。







例では、クラスはBaseObservableを継承し、コードではnotifyPropertyChanged()を呼び出しますが、これが唯一の方法ではありません。 ObservableFieldでフィールドをラップすることもでき、依存UI要素は自動的に更新されます。 しかし、この方法の柔軟性は低く、ほとんど使用していません。

これで、 xmlに次のようになります。







 <TextView android:text="@{viewmodel.name}" />
      
      





はるかに良いですね。







そのため、データとビューの間のレイヤーとして機能するViewModelクラスがあります 。 データ変換を処理し、どのフィールド(および関連するUI要素)を制御し、更新されると、一部のフィールドが他のフィールドに依存する方法のロジックが含まれます。 これにより、コードからxmlをクリアできます。 さらに、このクラスを使用して、 ビューからのイベント(クリックなど)を処理すると便利です。







そして、ここで考え浮かびます既にdatabindingがある場合、表示ロジックを含むViewModelクラスがあります。それでは、なぜMVVMパターンを使用しないのでしょうか?







この考えは必然的に生じます。 現時点で私たちが持っているものは、MVVMパターンと非常に近いためです。 それを簡単に見てみましょう。







MVVM



Model-View-ViewModelパターンには、3つの主要なコンポーネントがあります。









写真で見るこれらのコンポーネント間の関係と相互作用:













矢印は依存関係を示します。ViewはViewModelを認識し、ViewModelはModelを認識しますが、モデルはViewModelを認識せず、ViewModelはViewを認識しません。







プロセスは次のとおりです。ViewModelはデータをモデルに要求し、必要に応じて更新します。 モデルは、データがあることをViewModelに通知します。 ViewModelはデータを取得して変換し、UIのデータの準備ができたことをViewに通知します。 ViewModelとViewの関係は、データと表示を自動的にリンクすることによって行われます。 私たちの場合、これはデータバインディングライブラリを使用することで達成されます。 データバインディングでは 、ViewModelのデータを使用しビューが更新されます。







自動データバインディングの存在は、このパターンとPresentationModelおよびMVPパターンとの主な違いです(MVPでは、Presenterは提供されたインターフェイスを介してメソッドを呼び出すことでビューを変更します)。


Android MVVM



それで、プロジェクトでMVVMを使い始めました。 しかし、プログラミングではよくあることですが、理論と実践は同じものではありません。 そして、プロジェクトの完了後、私はまだ不満を感じていました。 このアプローチには何か問題があり、何かが好きではありませんでしたが、それが何であるか理解できませんでした







次に、AndroidでMVVM図を描くことにしました。













結果を考慮してください:







ViewModelには、 XMLでデータバインディングに使用されるフィールド( android:text=”@{viewmodel.username}”



)が含まれ、Viewによってトリガーされるイベントを処理します( android:onClick=”@{viewmodel::buttonClicked}”



)。 彼女はモデルにデータを要求し、それらを変換し、 データバインディングの助けを借りて、このデータを表示します。







フラグメントは、2つの役割を同時に実行します。初期化とシステムとの通信を提供する入力ポイントと、Viewです。







フラグメント(またはアクティビティ)がMVPおよびMVVMのパターンを理解する上でビューと見なされるという事実は、すでに一般的な慣行になっているため、これについては触れません。







アクティビティのねじれと再作成を乗り切るために、Viewが再作成されている間はViewModelをそのままにしておきます(この場合はFragment)。 これは、 短剣 スコープとカスタムスコープを使用して実現されます。 詳細については触れませんが、 短剣に関する多くの良い記事がすでに書かれています。 あなた自身の言葉では、次のことが起こります:









フラグメントがMvvmViewインターフェースを使用してViewModelに渡されるのなぜですか? これは、ビューでコマンドを「手動で」呼び出すために必要です。 データバインディングライブラリですべてを実行できるわけではありません。

システムがアプリケーションを強制終了した場合に状態を保存する必要がある場合、 savedInstanceStateフラグメントを使用してViewModel状態を保存および復元できます。







それが動作する方法です。







気配りのある読者は、「Fragmentをコンテナとして使用し、その中でsetRetainInstance(true)



を呼び出すことができるのに、なぜダガーカスタムスコープに setRetainInstance(true)



れるのですか?」と尋ねます。はい、できます。 しかし、図を描く際には、ビューとしてActivityまたはViewGroupを使用できることを考慮しました。







最近、私が描いた構造を完全に反映したMVVM実装の良い例を見つけました。 いくつかのニュアンスを除いて、すべてが非常にうまく行われました。 興味があれば見てください。


双対問題



図を描いてすべてを熟考した結果、このアプローチで作業している間、私にはまったく合わないことがわかりました。 もう一度図を見てください。 太い矢印「 データバインディング 」と「 表示する手動コマンド 」を参照してください。 ここにある。 ここで、さらに詳しく説明します。







databindingがあるため、ほとんどのデータはxmlを使用してViewにインストールできます(必要に応じて、必要なBindingAdapterを作成します)。 しかし、このアプローチに適合しない場合があります 。 これらには、ダイアログ、 トースト 、アニメーション、遅延アクション、およびビュー要素を含むその他の複雑なアクションが含まれます。







TextViewで例を思い出してください。







 <TextView android:text="@{viewmodel.name}" />
      
      





view.post(new Runnable())



を使用してこのテキストを設定する必要がある場合はどうなりますか? (私たちは、なぜだとは思わない、どう思うか)







「byPost」属性を作成するBindingAdapterを作成し、要素上のリストされた属性の存在を考慮に入れることができます。







 @BindingAdapter(value = {"text", "byPost"}, requireAll = true) public static void setTextByPost(TextView textView, String text, boolean byPost) { if (byPost) { textView.post(new Runnable { public void run () { textView.setText(text); } }) } else { textView.setText(text); } }
      
      





そして、TextViewの両方の属性が指定されるたびに、このBindingAdapterが使用されます。 属性をxmlに追加します。







 <TextView android:text="@{viewmodel.name}" bind:byPost="@{viewmodel.usePost}" />
      
      





ViewModelには、値を設定するときにview.post()



を使用する必要があることを示すプロパティがあります。 追加してください:







 public class UserViewModel extends BaseObservable { private String name; private boolean usePost = true; // only first time @Bindable public String getFullname() { return name; } @Bindable public boolean getUsePost() { return usePost; } public void setUser(User user) { name = user.firstname + user.lastname; notifyPropertyChanged(BR.name); notifyPropertyChanged(BR.usePost); usePost = false; } }
      
      





非常に単純なアクションを実装するために必要な量を確認してください。







したがって、このようなことをViewで行う方がはるかに簡単です。 つまり、フラグメントによって実装されているMvvmViewインターフェイスを使用し、Viewメソッドを呼び出します(通常はMVPで行われているのと同じ方法で)。







ここで、 双対性の問題が現れます。2つの異なる方法でViewを操作します 。 1つは(データの状態を介して)自動で、2つ目は(表示中のコマンドの呼び出しを介して)手動です。 個人的には好きではありません。


状態の問題



次に、別の問題について説明します。 電話の回転の状況を想像してください。







  1. アプリケーションを起動しました。 ViewModelとView(フラグメント)は生きています。
  2. 彼らは電話を回しました-フラグメントは死に、ViewModelは生き続けました。 彼女のバックグラウンドタスクはすべて引き続き機能します。
  3. 新しいフラグメントが作成され、結合されました。 ViewModelから受信した保存状態(フィールド)をデータバインディングで表示します 。 すべてがクールです。
  4. しかし、フラグメント(View)が切断されたときに、バックグラウンドプロセスがエラーで終了し、 トーストを表示したい場合はどうでしょうか。 フラグメント(Viewの役割を果たしている)は死んでおり、そのメソッドを呼び出すことはできません。
  5. この結果は失われます。


ViewModelフィールドのセットで表されるView状態だけでなく、ViewModelがViewで呼び出すメソッドも何らかの方法で保存する必要があることがわかります







この問題は、個々のこのような場合にViewModelにフラグフィールドを挿入することで解決できます。 あまり美しくなく、普遍的でもありません。 しかし、それは機能します。







状態について



状態の問題により、オブジェクト状態は状態を特徴付ける一連のパラメーター、またはオブジェクトを目的の状態にするために実行する必要のある一連のアクションの2つの方法で再作成できると考えるようになりました







ルービックキューブを想像してください。 彼の状態は、顔の1つに9色で記述できます。 そして、あなたは彼を初期状態から望ましい状態に導く一連の動きをすることができます。













1ターンだけかかる場合もあれば、9ターン以上かかる場合もあります。 状況に応じて、状態を説明する何らかの方法の方が良いか悪いかがわかります(必要なデータは少ない)。







モクシー



状態を再現する方法を考えたとき、Moxyライブラリーを思い出すしかありませんでした。 私の同僚は、MVPパターンとこのライブラリを使用してプロジェクトを並行して行いました。 詳細については説明しませんが、著者からの優れた記事は既にあります。







私の推論の文脈では、 Moxyの機能の 1つ興味深いものです。 このビューで呼び出される一連のコマンドとしてビューの状態を保存します 。 そして、私がそれについて最初に学んだとき、それは私には奇妙に思えました。







しかし、今、すべての考え(上記であなたと共有した)の後、これは非常に良い解決策だと思います。

なぜなら:









さらに、このアプローチは別のプラスを提供します。 Databinding Libraryと同様に、さまざまな状態の問題を独自の方法で解決します。 また、一連のメソッド呼び出しによって変更が再作成されるため、フィールドセットまたは状態の1つの名前に応じてUIを変更する大きなスイッチを記述する必要もありません。







それでも、Moxyについては何も言えません。 私の意見と同僚の意見では、今日、MVPパターンを扱うのに役立つ最高のライブラリです。 コード生成を使用して、開発者の労力を最小限に抑えます。 パターンの実装について考えることはできませんが、プロジェクトの機能について考えることはできません。 これはいいです。







しかし、MVPについては十分です。 それでも、私たちはMVVMについて話しているので、在庫を確認する時が来ました。







結論



私はMVVMをパターンとして気に入っており、その利点に異議を唱えません。 しかし、大部分は他のパターンと同じか、開発者の好みの問題です。 そして、主なプラスは、パターンそのものではなく、まだデータバインディングです。







MVVMへの共感に駆られて、プロジェクトを実装しました。 私は長い間トピックを研究し、熟考し、議論し、このパターンのマイナスのセットを自分で作りました:









はい、これらのマイナスに慣れることができます。 しかし、よく考えた結果、アプローチの断片化を引き起こすパターンを使用したくないという結論に達しました。 そして、MVPとMoxyを使用して次のプロジェクトを書くことにしました。







このパターンを使用してください-自分で決めてください。 しかし、私はあなたに警告しました。







PS:データバインディングライブラリ



おそらく、私たちが始めたものであるDatabinding Libraryで終わらせましょう。 私はまだ彼女が好きです。 しかし、私は限られた量でそれを使用するつもりです:









それだけです。 これは利点をもたらしますが、MVVMの方向には引き付けません。







データバインディングライブラリを使用する予定がある場合は、次の役立つ情報を参照してください。










All Articles