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


14

私は過去にMVPとMVCを使用しましたが、MVPのほうが実行の流れを非常によく制御できるので、私の意見ではMVPを好んでいます。

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

セクションA

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

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

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

セクションB

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

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

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

セクションC

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

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

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

セクションD

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

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

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

  3. メインビューであれ小さなビューであれ、それぞれのビューは、それぞれ独自のプレゼンターとモデルに配線されます。ビューコントロールを既存のフォームに文字通りドロップするだけで、機能の準備が整い、舞台裏でプレゼンターに配線するだけで済みます。

セクションE

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

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

  2. ビューとモデルにはインターフェースがあります。これにより、ビューとモデルが異なります。プレゼンターは、ビューおよびモデルオブジェクトを作成/指定され、それらの間でメッセージを渡すだけです。

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

個人的な考え

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

質問

MVPを正しく実装していますか?適切な方法はありますか?

バリエーションのあるマーティン・ファウラーの作品を読んだことがあり、MVCを始めたとき、コンセプトを理解していましたが、エントリポイントがどこにあるのか、すべてが独自の機能を持っていますが、オリジナルを制御して作成するものは元々理解できませんでしたMVCオブジェクトのセット。


2
この質問をする理由は、私がほとんど最初の試みでそれを正しくすることを探しているからです。同じパターンに異なるバリエーションを使用して6つのアプリケーションを作成するのではなく、標準のMVPを使用する方が望ましいです。
ジョンウィリス

完璧な...私は長い間それを尋ねたかった
キングキング

回答:


4

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

私にとって、プレゼンターはほとんど常にエントリーポイントであると感じています。UIをエントリポイントとして使用すると、UIに多くのロジックが配置され、コーディングを大幅に変更することなく新しいUIを置き換えることができなくなります。本当にそれがプレゼンターの仕事です。


たぶん、私が尋ねるべき質問は、MVPを使用する方法のいずれかが単に間違っていることです。さまざまなシナリオに適したバリエーションに同意しますが、一般的に受け入れられているアプローチがあるはずです。MVPの例はすべて、ドメインオブジェクトを編集して変更を保存するという単純な例であり、ほとんどすべてが同じニーズを持っています。それでも、同じ目的を持つこれらの例から、上記のバリエーションが生成されました。プレゼンターで処理されるビューで発生するイベントを使用してアプリの一部をコーディングしましたが、ビューにプレゼンターへの参照を保持させ、メソッドを直接呼び出すこともできます。
ジョンウィリス

ビューを単純化してユニットテストを行わないことを正当化しようとするすべての努力に同意し、発表者が制御する必要があります。これに関する私の唯一の懸念は(私の頭の中で間違っている可能性があると考える)、winforms / usercontrolsを親要素にリンクする必要があり、それを記述するためにプレゼンターで追加のロジックが必要かどうか疑問に思うことです。
-JonWillis

@Jon-MVPが間違っていますか?私の本では、ビューがプレゼンターについて知っているときになります。動作するという意味では「間違っている」わけではありませんが、MVPではなく、その時点で他の何かに変化しています。設計パターンの問題は、例が常に可能な限り簡潔でシンプルであることです。その後、初めてそれらを実装するために行くと、現実の世界が跳ね上がり、「本当のアプリはそのちっぽけな例よりもはるかに複雑です」と言います。それがあなたの成長の始まりです。あなたに合ったものとアプリの状況を見つけてください。
ウォルター

アドバイスをありがとう。大学でMVCを学んだことを覚えています。素晴らしく聞こえましたが、空白のキャンバスを使用すると、どこから始めて、それが実際にどのように機能するのか疑問に思います。MVPが間違っているという意味では、MVCのアイデア/バリエーションのいずれかが間違った方法、つまり、ビューがプレゼンターの動作を知っている場合に投稿したものです。または、ビューにプレゼンターや具体的なタイプなどのインターフェイスへの参照を含める必要があります。すべて同じ目的で機能するさまざまなバリエーションがあります。
ジョンウィリス

1
@Jon-あなたのバリエーションはすべてMVPの精神に沿っています。インターフェースまたは具象型を使用する限り、アプリの状況に依存します。アプリが非常に小さく、それほど複雑ではない場合、おそらくインターフェイスを追加する必要はありません。アプリが絶対にXアーキテクチャーを実装する必要があることが明確になるまで、物事を可能な限りシンプルに保つことが好きです。詳細については、この回答を参照してください:Programmers.stackexchange.com/questions/34547/…-
ウォルター

4

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

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

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

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

このパターンを特定するには、約6〜8か月かかりました。多くの部分がありますが、非常に柔軟で非常に強力です。特定の実装は非常に非同期でイベント駆動型であり、これは設計動作の副作用ではなく、他の要件の成果物である可能性があります。たとえば、VMの継承元であるベースクラスにスレッド同期を追加しました(イベントを発生させるために単にOnPropertyChangedメソッドを公開しました)。


あなたのMVPVMが商業的に興味があるかもしれないことを知っていますが、もしそれが大丈夫ならサンプルコードを提供できますか?サイドノートでは、モデルで何が行われ、プレゼンターで何が行われるかについて、どこで線を引きますか。プレゼンターは非常に基本的であり、ビューイベントを処理し、モデルを呼び出すだけです。データレイヤーにアクセスしてプレゼンターがビジネスオブジェクトをモデルに渡し、モデルがプレゼンターに置き換えられるまでです。
ジョンウィリス

うん、例を完成させてください。私は社内のトレーニングツールとして使用しているため、ビジネス向けに少し調整されます。
ブライアンベッチャー

ありがとう、私は週末にそれを理解しているかどうかを確認します
-JonWillis

@insta-リンクがダウンしています。どこかに再アップロードできますか?
イヴシェルプ

1
がらくた私はちょうど一週間前のようにそれを削除しました。数字:(
ブライアンベッチャー

2

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

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

私は次の自由を取りました。

  • リフレクションを使用すると、ビューメディエーターでプライベートプロパティを取得してフォームクラスに移動し、調停する各コントロールへの(プライベート)参照を取得できました。
  • リフレクションを使用して、メディエーター内のイベントシグネチャにコントロールを自動配線できます。これは一般的なものなので、senderパラメーターをオンにするだけです。
  • 通常、PureMvcは、派生メディエーターに、文字列の配列を返す関数で通知対象を定義することを望んでいます。私の興味はほとんど静的であり、メディエーターの興味を簡単に見たいので、メディエーターが特定の署名を持つメンバー変数のセットによってその興味を宣言できるように変更を行いました:_ _ _ <classname> _ <通知名>。このようにして、関数の内部を見る代わりにIDEメンバーツリーを使用して、何が起こっているのかを確認できます。

PureMvcでは、コマンドをエントリポイントとして使用できます。典型的なStartコマンドは、可能な範囲でモデルをセットアップし、MainFormを作成し、そのフォームのメディエーターを作成および登録してから、Application.Runを実行します。フォームに。

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.