11

私は、MVVMのコンテキスト内で、主にViewModelをいつ初期化するかという問題から生じるいくつかの異なる設計概念と戦っています。「初期化」に関してより具体的に言うと、選択値、セキュリティコンテキスト、および場合によっては数秒の遅延を引き起こす可能性のあるその他のものなどの値の読み込みを指します。

考えられる戦略:

  1. 引数をViewModelコンストラクターに渡し、コンストラクターにロードします。
  2. ViewModelでパラメーターなしのコンストラクターのみをサポートし、代わりにパラメーターを受け取ってロードを行う初期化メソッドをサポートします。
  3. オプション1と2の組み合わせ。引数はViewModelコンストラクターに渡されますが、Initializeメソッドが呼び出されるまでロードは延期されます。
  4. オプション3のバリエーションで、パラメーターがViewModelコンストラクターに渡される代わりに、プロパティに直接設定されます。

ViewModelプロパティのゲッターとセッターに影響を与える

初期化が延期される場合、ViewModelが、他の非同期で時間のかかる操作の場合と同じように、IsBusyプロパティが一般的に機能する利用可能と見なされる状態にあるかどうかを知る必要があります。ただし、これは、ViewModelのほとんどのプロパティがモデルオブジェクトから取得した値を公開するため、モデルが使用可能であることを確認するために、常に次のタイプの配管を作成する必要があることを意味します。

public string Name
{
    get 
    {  
        if (_customerModel == null) // Check model availability
        {
            return string.Empty;
        }

        _customerModel.Name;
    }
}

チェックは簡単ですが、INPCやその他のタイプの必需品の配管に追加するだけで、ViewModelの作成と保守がやや面倒になります。場合によっては、プロパティゲッターから戻るための合理的なデフォルトが常にあるとは限らないため、さらに問題が発生します。これは、ブールプロパティIsCommercialAccountの場合であり、使用可能なモデルがない場合、trueまたはを返す意味がありません。 null可能性など、他の多くの設計上の質問に疑問を投げかけるのは誤りです。上記のオプション1の場合、すべてをコンストラクターに渡してロードした場合、ビューからのNULL ViewModelのみを考慮する必要があり、ViewModelがnullでない場合は、初期化されることが保証されます。

遅延初期化のサポート

オプション4を使用すると、ViewModelの基本クラスに実装できるISupportInitializeに依存して、ViewModelが初期化されているかどうかを通知する一貫した方法を提供し、標準メソッドBeginInitを介して初期化を開始することもできます。これはオプション2および3の場合にも使用できますが、すべての初期化パラメーターがすべて1つのアトミックトランザクションで設定されている場合はあまり意味がありません。少なくともこのように、上記の状態は次のようなものに変わる可能性があります

デザインがIoCに与える影響

IoCに関しては、オプション1と3は一般的に好まれるコンストラクター注入を使用して実行でき、オプション2と4はそれぞれメソッドとプロパティ注入を使用して実行できることを理解しています。ただし、私の懸念はIoCやこれらのパラメーターの受け渡し方法ではなく、全体的な設計と、それがViewModelの実装とパブリックインターフェイスにどのように影響するかです。将来。

妥当性

3つのオプションはすべて、テスト容易性の概念を等しくサポートしているようです。これは、これらのオプションを決定するのにあまり役立ちませんが、オプション4では、プロパティの適切な動作を保証するために、より広範なテストセットが必要になる可能性があり、その動作は初期化状態に依存します。 。

コマンド能力

オプション2、3、および4はすべて、ViewModelの初期化メソッドを呼び出すためにViewの背後にあるコードを必要とするという副作用がありますが、これらは必要に応じてコマンドとして公開される可能性があります。ほとんどの場合、以下のように、構築直後にこれらのメソッドを呼び出してロードする可能性があります。

var viewModel = new MyViewModel();
this.DataContext = viewModel;
// Wrap in an async call if necessary
Task.Factory.StartNew(() => viewModel.InitializeWithAccountNumber(accountNumber));

他のいくつかの考え

MVVMデザインパターンを使用して作業しているため、これらの戦略のバリエーションを試しましたが、ベストプラクティスについてはまだ結論を出していません。コミュニティの意見を聞き、ViewModelを初期化するための最善の方法、または使用できない状態にあるときにプロパティを処理するための最善の方法について、合理的なコンセンサスを見つけようと試みたいと思います。

理想的なケースは、ViewModel自体がさまざまな状態を表すさまざまなViewModelオブジェクトと交換されるStateパターンを使用することです。そのため、ViewModelのIsBusyプロパティのニーズの1つを削除するビジー状態を表す一般的なBusyViewModel実装を作成し、次のViewModelの準備ができたら、Viewでスワップアウトして、ViewModelが概説された状態に従うことができるようにします。オプション1では、構築中に完全に初期化されます。これにより、状態遷移の管理を誰が担当するかについていくつかの質問が残ります。たとえば、BackgroundWorkerまたは初期化自体を実行し、準備ができたときに内部ViewModelを提示するタスクに類似したものを抽象化するのは、BusyViewModelの責任である可能性があります。一方、ビューでDataContextを交換するには、ビューでイベントを処理するか、ビューのDataContextプロパティへのアクセスを制限してBusyViewModelに制限し、従来の状態パターンの意味で設定できるようにする必要があります。人々がこれらの線に沿ってやっているのと同じようなことがあれば、私のグーグル検索はまだあまり出ていないので、私は間違いなく知りたいです。

4

1 に答える 1

5

ビューモデルを作成する場合でも、他のタイプのクラスを作成する場合でも、オブジェクト指向設計に対する私の一般的なアプローチは次のとおりです。コンストラクターに渡すことができるものはすべて、コンストラクターに渡す必要があります。これにより、ある種のIsInitialized状態を持つ必要性が減り、オブジェクトの複雑さが軽減されます。特定のフレームワークでは、このアプローチに従うのが難しい場合があります。たとえば、IoCコンテナー(コンストラクターの注入を許可する必要があります)ですが、それでも原則としてこれを順守します。

于 2012-07-23T05:50:52.687 に答える