web-dev-qa-db-ja.com

MVVM:ViewModelとビジネスロジック接続

MVVMパターンを使用していくつかのプロジェクトを行った後、私はまだViewModelの役割に苦労しています。

過去に行ったこと:モデルをデータコンテナとしてのみ使用する。 ViewModelでデータを操作するロジックを配置します。 (ビジネスロジックは正しいですか?)欠点:ロジックは再利用できません。

私が今試していること:ViewModelを可能な限り薄く保つ。すべてのロジックをモデルレイヤーに移動します。 ViewModelでのみプレゼンテーションロジックを保持します。短所:データがモデルレイヤー内で変更されると、UI通知が非常に苦痛になります。

したがって、より明確にするために例を示します。

シナリオ:ファイルの名前を変更するツール。クラス:File:各ファイルを表します。ルール:ファイルの名前を変更するロジックが含まれています。

アプローチ1に従っている場合:ファイル、ルール、およびビューのViewModelの作成-> RenamerViewModel。 RenamerViewModelにすべてのロジックを配置する:FileViewModelとRuleViewModelのリストと進行するロジックを含む。簡単で高速ですが、再利用できません。

アプローチ2に従っている場合:ファイルのリストを含む新しいモデルクラス->リネーマーを作成する場合、各ファイルを相互に関連付けて各ルールを適用するためのルールと進行中のロジック。ファイル、ルール、リネーマーのビューモデルを作成します。現在、RenamerViewModelにはRenamer Modelのインスタンスと、RenamerのFile und Rule Listをラップする2つのObservableCollectionのみが含まれています。ただし、ロジック全体はRenamer Modelにあります。そのため、メソッド呼び出しによって一部のデータを操作するためにリネームモデルがトリガーされた場合、ViewModelにはどのデータが操作されているのかがわかりません。モデルにはPropertyChange通知が含まれていないため、これは回避します。そのため、ビジネスロジックとプレゼンテーションロジックは分離されていますが、これによりUIへの通知が難しくなります。

40
JDeuker

ビューモデル内にビジネスロジックを配置することは物事を行うための非常に悪い方法ですので、私はすぐに決して言わないつもりです 2番目のオプション。

モデル内にロジックを配置することは、はるかに合理的であり、素晴らしい開始アプローチです。欠点は何ですか?あなたの質問は言う

そのため、メソッド呼び出しによって一部のデータを操作するためにリネームモデルがトリガーされた場合、ViewModelにはどのデータが操作されているのかがわかりません。モデルにはPropertyChange通知が含まれていないため、これは回避します。

さて、モデルにINotifyPropertyChangedを実装させることで、より良いものに進むことができます。ただし、それができない場合があることは事実です。たとえば、モデルは、ツールによってプロパティが自動生成され、変更通知を発生させない部分クラスである場合があります。それは残念ですが、世界の終わりではありません。

何かを購入したい場合は、誰かがそれを支払わなければなりません。そのような通知を行うモデルではない場合、2つの選択肢しかありません。

  1. ビューモデルは、モデル上のどの操作が(おそらく)変更を引き起こすかを認識し、そのような各操作の後に状態を更新します。
  2. 他の誰かは、どの操作が変更を引き起こすかを知っており、モデルが変更をラップした後にその状態を更新するようビューモデルに通知します。

事実上、ビューモデル内に「ビジネスロジック」を配置することに戻っているため、最初のオプションはやはり悪い考えです。ビューモデルにビジネスロジックをall入れるほど悪くはありませんが、それでもです。

2番目のオプションは、より有望です(そして、残念ながら、実装するためにより多くの作業があります):

  • ビジネスロジックの一部を別のクラス(「サービス」)に入れます。このサービスは、必要に応じてモデルインスタンスを操作して、実行するすべてのビジネスオペレーションを実装します。
  • これは、モデルプロパティがいつ変更されるかをサービスが認識していることを意味します(これは問題ありません:モデル+サービス==ビジネスロジック)。
  • このサービスは、変更されたモデルに関する通知をすべての関係者に提供します。ビューモデルはサービスに依存し、これらの通知を受け取ります(したがって、「モデル」モデルが更新されたことがわかります)。
  • ビジネスオペレーションもサービスによって実装されるため、これは非常に自然なままです(たとえば、ビューモデルでコマンドが呼び出されると、反応はサービスの適切なメソッドを呼び出します。ビューモデル自体はビジネスロジックを認識しないことに注意してください)。

そのような実装の詳細については、私の回答 here および here も参照してください。

49
Jon

両方のアプローチは有効ですが、3番目のアプローチがあります。モデルとVMレイヤーの間にサービスを実装します。モデルを愚かにしたい場合は、再利用可能な方法でビジネスルールを実施できます。

モデルにはPropertyChange通知が含まれていないため、それを避けるため

なぜこれを避けているのですか?誤解しないでください。私はモデルをできるだけ馬鹿にする傾向がありますが、モデルに変更通知を実装すると役立つ場合があり、System.ComponentModelするとき。それは完全にUIに依存しません。

12
Kent Boogaart

私は次のことをします

  1. XAMLビューロジックのみで表示

  2. クリックハンドラーを処理し、新しいビューモデルを作成するViewModel。ルーティングイベントなどを処理します。

  3. モデルデータの検証に関するデータコンテナーおよびビジネスロジックであるモデル。

  4. モデルにデータを取り込むサービス。たとえば、Webサーバーを呼び出し、ディスクからロードし、ディスクに保存します。例によっては、モデルとサービスの両方でIPropertyChangedを実装することがよくあります。または、代わりにイベントハンドラーがある場合があります。

複雑なアプリケーションの場合は、別のレイヤーが必要です。モデル+サービス、ビュー、ビューモデルと呼びます。このサービスはビジネスロジックを抽象化し、モデルインスタンスを依存関係として取得するか、モデルを作成します。

1
rolls

ModelとViewModelの両方にIDataErrorInfoを実装することもできますが、Modelでのみ検証を行うため、Modelでのみビジネスルールを実装する方法が簡単になります...

例:

ViewModel:

...

private Person person;

...

string IDataErrorInfo.this[string propertyName]
{
    get
    {
        string error = (person as IDataErrorInfo)[propertyName];
        return error;
    }
}

モデル:

public class Person:INotifyPropertyChanged,IDataErrorInfo
{

...

   string IDataErrorInfo.this[string propertyName]
   {
        get { return this.GetValidationError(propertyName); }
   }

...

   string GetValidationError(string propertyName)
   {
        if(propertyName == "PersonName")
             //do the validation here returning the string error
   }
}

さらに、MVCVMパターンを見てください。実際にそれを使用していますが、ビジネスロジックをモデルやビューモデルではなくコントローラークラスに抽象化するのはかなり問題ありません。

0
Rafael A. M. S.