web-dev-qa-db-ja.com

WinformsソリューションのMVPを設定するにはどうすればよいですか?

私は過去にMVPとMVCを使用しましたが、MVPは実行フローを非常によく制御するため、MVPを好みます。

インフラストラクチャ(データストア/リポジトリクラス)を作成し、サンプルデータをハードコーディングするときに問題なく使用できるようにしたので、GUIに移動してMVPを準備しています。

セクションA

  1. MVPがビューをエントリポイントとして使用するのを見てきました。つまり、ビューコンストラクターメソッドでは、プレゼンターを作成し、モデルを作成して、必要に応じてイベントを関連付けます。

  2. また、プレゼンターをエントリポイントとして見ました。ビュー、モデル、プレゼンターが作成され、このプレゼンターには、コンストラクターでビューとモデルオブジェクトが与えられ、イベントを結び付けます。

  3. 2と同じですが、モデルはプレゼンターに渡されません。代わりに、モデルは静的クラスであり、メソッドが呼び出され、応答が直接返されます。

セクションB

ビューとモデルの同期を保つという点で、私は見ました。

  1. ビューの値が変更されたとき。つまり、.Net/C#のTextChangedイベント。これにより、モデルに渡されるDataChangedEventが起動され、常に同期が保たれます。そして、モデルが変更される場所、つまり、モデルがリッスンするバックグラウンドイベントの場合、DataChangedEventを発生させるのと同じ考え方でビューが更新されます。ユーザーが変更をコミットする場合は、SaveEventが発生し、モデルに渡されて保存されます。この場合、モデルはビューのデータを模倣し、アクションを処理します。

  2. #b1と同様ですが、ビューは常にモデルと同期しません。代わりに、ユーザーが変更をコミットしたい場合は、SaveEventが呼び出され、プレゼンターが最新の詳細を取得してモデルに渡します。この場合、モデルは、アクションを実行する必要があるまでビューデータを認識しません。その場合、必要なすべての詳細が渡されます。

セクションC

ビュー内のビジネスオブジェクトの表示、つまりプリミティブデータ(int、double)ではないオブジェクト(MyClass)

  1. ビューには、ドメイン/ビジネスオブジェクトとして表示されるすべてのデータのプロパティフィールドがあります。 _view.Animals_などは、ビューがこれらをTreeViewのノードに処理しても、_IEnumerable<IAnimal>_プロパティを公開します。次に、選択した動物に対して、SelectedAnimalIAnimalプロパティとして公開します。

  2. ビューはドメインオブジェクトの知識がなく、プリミティブ/フレームワーク(.Net/Java)に含まれるオブジェクトタイプのプロパティのみを公開します。この場合、プレゼンターはアダプターオブジェクトをドメインオブジェクトに渡し、アダプターは特定のビジネスオブジェクトをビューに表示されるコントロールに変換します。この場合、アダプターはビューだけでなく、ビュー上の実際のコントロールにアクセスできる必要があるため、より密接に結合されます。

セクションD

単一のコントロールを作成するために使用される複数のビュー。つまり、さまざまなタイプのオブジェクトを保存するような単純なモデルを持つ複雑なビューがあります。あなたは、適切なコントロールが表示されているアイテムをクリックするたびに横にメニューシステムを持つことができます。

  1. ビューインターフェースを介して公開される個々のコントロールのすべてを含む1つの巨大なビューを作成します。

  2. いくつかのビューがあります。メニューとブランクパネルのビューが1つあります。このビューは、他の必要なビューを作成しますが、表示しません(visible = false)。このビューは、ビューに含まれる各ビュー(つまり、子ビュー)のインターフェースも実装しているため、1人の発表者に公開できます。ブランクパネルには、他のビュー(Controls.Add(myview))および(_(myview.visible = true_)が表示されます。これらの「子」ビューで発生したイベントは親ビューによって処理されます。親ビューはイベントをプレゼンターに渡し、その逆も同様にイベントを子要素に提供します。

  3. メインの親ビューまたは小さい子ビューのそれぞれのビューは、それぞれ独自のプレゼンターとモデルに関連付けられています。文字通り、ビューコントロールを既存のフォームにドロップするだけで機能を利用できるようになり、舞台裏のプレゼンターへの配線が必要になります。

セクションE

すべてにインターフェースがある場合、上記の例でMVPがどのように実行されるかに基づいて、相互互換性がない可能性があるため、この回答に影響します。

  1. すべてにビュー、プレゼンター、モデルというインターフェースがあります。これらのそれぞれは明らかに具体的な実装を持っています。具体的なビュー、モデル、プレゼンターが1つしかない場合でも。

  2. ビューとモデルにはインターフェースがあります。これにより、ビューとモデルを区別できます。プレゼンターは、ビューとモデルオブジェクトを作成/与えられ、それらの間でメッセージを受け渡す役割を果たします。

  3. ビューのみにインターフェースがあります。モデルには静的メソッドがあり、作成されないため、インターフェースは必要ありません。別のモデルが必要な場合は、プレゼンターが静的クラスメソッドの別のセットを呼び出します。静的であるため、モデルにはプレゼンターへのリンクがありません。

個人的な考え

私が提示したすべてのさまざまなバリエーション(ほとんどの場合、おそらく何らかの形で使用したもの)のうち、まだまだあると思います。ビジネスロジックをMVP、B2以外で再利用可能にしておくため、データの重複や発生するイベントが少ないA3を好みます。 C1は別のクラスに追加しないため、少量の単体テスト可能なロジックをビュー(ドメインオブジェクトの視覚化方法)に確実に配置しますが、これはコードで確認することも、アプリケーションで単に表示することもできます。ロジックが複雑だった場合、アダプタークラスに同意しますが、すべてのケースに同意するわけではありません。セクションDの場合、D1は少なくともメニューの例としては大きすぎるビューを作成するように感じます。以前にD2とD3を使用しました。 D2の問題は、プレゼンターとの間のイベントを正しい子ビューにルーティングするために多くのコードを書く必要があり、ドラッグ/ドロップ互換ではないことです。新しいコントロールごとに、単一のプレゼンターをサポートするために追加の配線が必要です。 D3は私の好みの選択ですが、たとえビューが非常に単純であったり、再利用する必要がない場合でも、ビューを処理するプレゼンターおよびモデルとしてさらに多くのクラスを追加します。状況に応じて、D2とD3の混合が最適だと思います。セクションEについては、インターフェイスを持つすべてのものはやり過ぎである可能性があると思います。ドメイン/ビジネスオブジェクトに対して既に行っているので、そうすることによって「デザイン」に利点がないことがよくありますが、テストでオブジェクトをモックするのには役立ちます。個人的には、E2をクラシックソリューションと見なしていますが、以前に取り組んだ2つのプロジェクトでE3が使用されているのを見てきました。

質問

MVPを正しく実装していますか?それについて正しい方法はありますか?

バリエーションのあるMartin Fowlerの作品を読みました。MVCを始めたときのことを覚えていますが、コンセプトを理解しましたが、最初はエントリポイントがどこにあるのかを理解できませんでした。すべてに独自の機能がありますが、オリジナルを制御および作成するものMVCオブジェクトのセット。

14
JonWillis

ここに提示する内容の多くは非常に合理的で健全です。いくつかの選択肢は、アプリケーションの詳細と、どれが「適切」であるかによって異なります。ほとんどの場合と同様に、正解は1つではありません。いくつかの選択はここで意味があり、それらの選択は次のアプリケーションと状況では完全に間違っている可能性があります。アプリの詳細をいくつか知らなくても、あなたは正しい方向に進んでいて、健全で思慮深い決定を下したと思います。

私にとっては、ほとんどの場合、プレゼンターがエントリーポイントである必要があると感じています。エントリポイントとしてUIを使用すると、UIにロジックが多すぎて、コーディングに大きな変更を加えずに新しいUIを代用することができなくなります。そして本当にISプレゼンターの仕事です。

4
Walter

.NET 2.0 Winformsアプリでは、変更された形式のMVPを使用しています。欠けていた2つの部分は、WPF ViewModelの変更されたアダプターと、データバインディングの追加でした。特定のパターンはMVPVMです。

デザイナーとの親しみやすさのためにビューファーストで配線されるカスタムユーザーコントロールを除いて、ほとんどすべてのケースでプレゼンターファーストとして配線します。依存性注入、コード生成されたViewModel、プレゼンターにはBDD、モデルにはTDD/TEDを使用します。

VMは、変更されたときにPropertyChangedを発生させる、大規模でフラットな一連のプロパティです。これらをコード(およびそれに関連付けられた実行ユニットテスト)で生成するのは非常に簡単でした。ユーザーが操作できるコントロールの読み取りと書き込み、および有効なステータスの制御にこれらを使用します。 ViewModelはViewと連動しています。これは、他のすべての近くでデータバインディングを使用しているためです。

ビューには、VMができないことを達成するためのメソッドが時々あります。これは通常、アイテムの可視性(WinFormsはそれについてうるさい場合があります)とデータバインドを拒否するものを制御しています。ビューは常に、「Login」や「Restart」などのイベントを公開し、適切なEventArgsを使用してユーザーの行動に影響を与えます。「View.ShowLoginBox」などのハックを使用する必要がない限り、ビューが満たされる限り、ビューは完全に交換可能です。一般的な設計要件。

このパターンを特定するのに約6〜8か月かかりました。多くの要素がありますが、非常に柔軟で非常に強力です。私たちの特定の実装は非常に非同期でイベント駆動型であり、設計動作の副作用ではなく、他の要件のアーチファクトである可能性があります。たとえば、VMが継承するベースクラス(イベントを発生させるためにOnPropertyChangedメソッドを公開するだけ)にスレッド同期を追加しました-そして、マルチスレッドのプレゼンターとモデルを使用できるようになりました。

4
Bryan Boettcher

.Net用に変更され、自分で拡張されたバージョンのPureMvcを使用しています。

私はPureMvcをFlexアプリケーションで使用することに慣れていました。それは必要最低限​​のタイプのフレームワークなので、カスタマイズしたい場合はかなり簡単に適応できます。

私は以下の自由をそれに取りました:

  • リフレクションを使用して、view-mediatorでプライベートプロパティを取得してフォームクラスに移動し、仲介している各コントロールへの(プライベート)参照を取得することができました。
  • リフレクションを使用すると、メディエーターのイベントシグネチャ(汎用)にコントロールを自動配線できるため、送信者パラメーターをオンにするだけです。
  • 通常、PureMvcは、派生メディエーターが文字列の配列を返す関数で通知の対象を定義することを望んでいます。私の関心はほとんど静的であり、メディエーターの関心をより簡単に確認できる方法を求めていたため、メディエーターが特定のシグネチャーを持つメンバー変数のセットによって関心を宣言できるように変更を加えました:_ _ _ <classname> _ <通知名>。このようにして、関数の内部を調べる代わりに、IDEメンバーツリーを使用して何が起こっているのかを確認できます。

PureMvcでは、コマンドをエントリポイントとして使用できます。通常のStartコマンドは、可能な範囲でモデルを設定し、MainFormを作成し、そのフォームにメディエーターを作成して登録し、次にApplication.Runを実行します。フォーム上。

フォームのメディエーターは、すべてのサブメディエーターのセットアップを担当します。これの一部は、リフレクショントリックを使用して自動化できます。

私が使用しているシステムは、あなたの意味が理解できれば、ドラッグ/ドロップ互換です。実際のフォームはすべてVSで作成されますが、私の経験は静的に作成されたコントロールを持つフォームでのみです。動的に作成されたメニュー項目のようなものは、そのメニューまたはサブメニューのメディエーターを少し調整することで実現可能に見えます。毛むくじゃらになるのは、メディエーターにフックする静的ルート要素がなく、動的な「インスタンス」メディエーターを作成するときです。

2
Mark