web-dev-qa-db-ja.com

MVVMとサービスパターン

MVVMパターンを使用してWPFアプリケーションを構築しています。現在、私のviewmodelsはサービスレイヤーを呼び出してモデルを取得し(viewmodelには関係ありません)、それらをviewmodelsに変換します。コンストラクターインジェクションを使用して、必要なサービスをビューモデルに渡します。

これは簡単にテストでき、依存関係の少ないビューモデルでうまく機能しますが、複雑なモデルのビューモデルを作成しようとするとすぐに、多数のサービスが注入されたコンストラクターがあります(各依存関係と使用可能なすべての値のリストを取得するためのコンストラクター)たとえば、itemsSourceにバインドします)。そのような複数のサービスをどのように処理し、それでも簡単に単体テストできるビューモデルを持っている方法を知りたいです。

私はいくつかの解決策を考えています:

  1. インターフェイスとして利用可能なすべてのサービスを含むサービスシングルトン(IServices)を作成します。例:Services.Current.XXXService.Retrieve()、Services.Current.YYYService.Retrieve()。そうすれば、大量のサービスパラメータが含まれる巨大なコンストラクタがなくなります。

  2. ViewModelによって使用されるサービスのファサードを作成し、このオブジェクトをビューモデルのctorに渡します。しかし、それから、複雑なビューモデルごとにファサードを作成する必要があります。

この種のアーキテクチャを実装する「正しい」方法は何だと思いますか?

14
alfa-alfa

実際、これらのソリューションはどちらも悪いです。

インターフェイスとして利用可能なすべてのサービスを含むサービスシングルトン(IServices)を作成します。例:Services.Current.XXXService.Retrieve()、Services.Current.YYYService.Retrieve()。そうすれば、大量のサービスパラメータが含まれる巨大なコンストラクタがなくなります。

これは本質的に、アンチパターンである Service Locator Pattern です。これを行うと、プライベート実装を確認しないと、ビューモデルが実際に依存しているものを理解できなくなり、テストやリファクタリングが非常に困難になります。

ViewModelによって使用されるサービスのファサードを作成し、このオブジェクトをビューモデルのctorに渡します。しかし、それから、複雑なビューモデルごとにファサードを作成する必要があります。

これはアンチパターンではありませんが、コードのにおいです。基本的には パラメータオブジェクト を作成していますが、POリファクタリングパターンのポイントは、が頻繁にさまざまな場所で使用されるパラメータセットを処理することですただし、このパラメータは一度しか使用されません。あなたが言及したように、それは実際の利益のために多くのコード膨張を作成し、多くのIoCコンテナでニースを演じません。

実際、上記の戦略はどちらも全体的な問題を見落としています。つまり、ビューモデルとサービスの結合が高すぎるです。単純にhidingサービスロケータまたはパラメータオブジェクトのこれらの依存関係は、ビューモデルが依存する他のオブジェクトの数を実際に変更しません。

これらのビューモデルの1つを単体テストする方法を考えてください。セットアップコードはどのくらいの大きさになりますか?それが機能するために何個を初期化する必要がありますか?

MVVMを使い始めた多くの人は、画面全体のビューモデルを作成しようとしますが、これは基本的に間違ったアプローチです。 MVVMはすべてcompositionに関するものであり、多くの機能を備えた画面は、それぞれが1つまたはいくつかの内部モデル/サービスのみに依存するいくつかの異なるビューモデルで構成される必要があります。彼らが互いに通信する必要がある場合は、pub/sub(メッセージブローカー、イベントバスなど)を介して行います。

実際に必要なことは、ビューモデルをリファクタリングして、依存関係が少なくなるようにすることです。次に、「画面」を集約する必要がある場合は、別のビューモデルを作成して、小さいビューモデルを集約します。この集約ビューモデルは、それ自体でそれほど多くのことを行う必要がないため、理解とテストも非常に簡単です。

これが適切に行われていれば、コードを見るだけで明らかなはずです。短く、簡潔で、特定された、テスト可能なビューモデルがあるからです。

22
Aaronaught

私はこれについて本を書くことができた...実際私は;)

まず第一に、物事を行う普遍的に「正しい」方法はありません。他の要因を考慮する必要があります。

サービスの粒度が細かすぎる可能性があります。特定のビューモデルまたは関連するビューモデルのクラスターでさえ使用するインターフェースを提供するファサードでサービスをラップすることは、より良いソリューションである可能性があります。

さらに単純なのは、すべてのビューモデルが使用する単一のファサードにサービスをラップすることです。もちろん、それは、平均的なシナリオでは多くの不要な機能を備えた非常に大きなインターフェースになる可能性があります。ただし、システム内のすべてのメッセージを処理するメッセージルーターと同じです。

実際、多くのアーキテクチャが最終的に進化するのを目にしたのは、Event Aggregatorパターンのようなものを中心に構築されたメッセージバスです。テストクラスはリスナーをEAに登録し、それに応じて適切なイベントを発生させるだけなので、テストは簡単です。しかし、それは成長するのに時間がかかる事前のシナリオです。私は統一ファサードから始めて、そこから行くと言います。

1
Michael Brown

両方を組み合わせませんか?

ファサードを作成し、ビューモデルが使用するすべてのサービスを配置します。次に、悪いS Wordなしで、すべてのyourviewmodelsに単一のファサードを設定できます。

または、コンストラクターインジェクションの代わりにプロパティインジェクションを使用できます。ただし、それらが適切に注入されていることを確認する必要があります。

0
Euphoric