Model-View-Presenter実装の考え方


34

UIとモデルの間の適切なデカップリングを実装する方法を十分に理解しようとしていますが、行を分割する場所を正確に把握するのに苦労しています。

私はModel-View-Presenterを見てきましたが、それをどのように実装するのか正確にはわかりません。たとえば、私のビューには複数のダイアログがあります。

  • 各ダイアログのインスタンスを持つViewクラスが必要ですか?その場合、ダイアログはプレゼンターとどのように対話する必要がありますか?すなわち。個々のダイアログがプレゼンターを介してモデルからデータを要求する必要がある場合、ダイアログはプレゼンターへの参照をどのように取得する必要がありますか?建設中に与えられたビューへの参照を介して?
  • 私は多分ビューが静的なクラスであるべきだと思っていましたか?次に、GetViewダイアログとそこからプレゼンターを取得します...
  • ビューとモデルの所有権を持つプレゼンターをセットアップすることを考えていました(ビューを持つプレゼンターとプレゼンターがモデルを持つのとは対照的に)、プレゼンターはビューのイベントのコールバックを登録しますが、それは多くのように見えますより結合された(または少なくとも言語に依存)

私がしようとしている:

  1. これを可能な限り分離する
  2. 理想的には、プレゼンター/モデルを他の言語のビューと結合することを可能にします(言語間で大量のことをやったことはありませんが、可能な限り、特にvoid(void)私ができる限り、少なくともC#アプリでC ++ライブラリ...
  3. コードを簡潔かつシンプルに保つ

だから..相互作用をどのように処理すべきか提案はありますか?



1
私は.. ..私は少し速いそれを発見し、ハイレベル、私は可能な限り小さなカップリングなどで大規模なプロジェクトで複数のダイアログを処理する方法をよりよく理解するために探しています持っている
trycatch

回答:


37

滑りやすい斜面へようこそ。この時点で、すべてのモデルとビューの相互作用には無限のバリエーションがあることに気付きました。MVC、MVP(Taligent、Dolphin、Passive View)、MVVMなどがあります。

モデルビュープレゼンターパターンは、ほとんどのアーキテクチャパターンと同様、多くの種類と実験に開放されています。すべてのバリエーションに共通することの1つは、ビューとモデルの間の「仲介者」としてのプレゼンターの役割です。最も一般的な2つは、パッシブビュー監督プレゼンター/コントローラー -[ Fowler ]です。パッシブビューは、ユーザーとプレゼンターの間の非常に浅いインターフェイスとしてUIを扱います。プレゼンターに多くの責任を委任するロジックが含まれていても、ほとんど含まれていません。プレゼンター/コントローラーの監督多くのUIフレームワークに組み込まれているデータバインディングを利用しようとします。UIはデータの同期を処理しますが、プレゼンター/コントローラーはより複雑なロジックにステップインします。どちらの場合でも、モデル、ビュー、プレゼンターはトライアドを形成します

これを行うには多くの方法があります。これは、各ダイアログ/フォームを異なるビューとして扱うことで処理されるのを見るのが非常に一般的です。多くの場合、ビューとプレゼンターの間には1対1の関係があります。これは難しい、速い規則ではありません。1人のプレゼンターが複数の関連するビューを処理したり、その逆を行うことはよくあります。それはすべて、ビューの複雑さとビジネスロジックの複雑さに依存します。

ビューとプレゼンターが相互に参照を取得する方法については、これをワイヤリングと呼びます。次の3つの選択肢があります。

ビューはプレゼンターへの参照を保持します
フォームまたはダイアログはビューを実装します。フォームには、直接関数呼び出しを使用してプレゼンターに委任するイベントハンドラーがあります。

MyForm.SomeEvent(Sender)
{
  Presenter.DoSomething(Sender.Data);
}

プレゼンターにはビューへの参照がないため、ビューはデータを引数として送信する必要があります。プレゼンターは、ビューがリッスンする必要があるイベント/コールバック関数を使用して、ビューとやり取りできます。

プレゼンターはビューへの参照を保持します
シナリオでは、ビューはユーザーに表示するデータのプロパティを公開します。プレゼンターはイベントをリッスンし、ビューのプロパティを操作します。

Presenter.SomeEvent(Sender)
{
  DomainObject.DoSomething(View.SomeProperty);
  View.SomeOtherProperty = DomainObject.SomeData;
}

両方とも循環参照を形成する相互参照を保持します。
このシナリオは、実際には他のシナリオよりも作業が簡単です。ビューは、プレゼンターのメソッドを呼び出すことでイベントに応答します。プレゼンターは、公開されたプロパティを使用してビューからデータを読み取り/変更します。

View.SomeEvent(Sender)
{
  Presenter.DoSomething();
}

Presenter.DoSomething()
{
  View.SomeProperty = DomainObject.Calc(View.SomeProperty);
}

MVPパターンで考慮すべき他の問題があります。作成順序、オブジェクトの有効期間、配線が行われる場所、MVPトライアド間の通信ですが、この答えはすでに十分に長くなっています。


1
これは間違いなく役立ちます。トライアドと生涯の間のコミュニケーションは、私がこれのいくつかを把握している今、私が現在問題を抱えているところです。
-trycatch

8

誰もが言っているように、多数の意見がありますが、それらのどれも正しいものでも間違っているものでもありません。無数のパターンに入ることなく、MVPのみに焦点を当てることなく、実装に関するいくつかの提案があります。

それらを分離してください。ビューは、ビューとプレゼンターの間の結合を形成するインターフェースを実装する必要があります。ビューはプレゼンターを作成し、プレゼンターに自身を挿入し、プレゼンターがビューと対話するために提供するメソッドを公開します。ビューは、これらのメソッドまたはプロパティを任意の方法で実装する責任があります。通常、ビューは1つ、プレゼンターは1つですが、場合によっては、1つのプレゼンター(Web、wpfなど)という多くのビューを使用できます。ここで重要なのは、プレゼンターがUIの実装について何も知らず、インターフェイスを介してビューとのみ対話することです。

以下に例を示します。最初に、ユーザーにメッセージを表示する簡単なメソッドを持つビュークラスがあります。

interface IView
{
  public void InformUser(string message);
}

これがプレゼンターです。プレゼンターはIViewをコンストラクターに取り込むことに注意してください。

class Presenter
{
  private IView _view;
  public Presenter(IView view)
  {
    _view = view;
  }
}

次に、実際のユーザーインターフェイスを示します。これは、ウィンドウ、ダイアログ、Webページなどです。重要ではありません。ビューのコンストラクターは、自身に注入することでプレゼンターを作成することに注意してください。

class View : IView
{
  private Presenter _presenter;

  public View()
  {
    _presenter = new Presenter(this);
  }

  public void InformUser(string message)
  {
    MessageBox.Show(message);
  }
}

プレゼンターは、ビューが実行するメソッドをビューがどのように実装するかを気にしません。プレゼンターが知っている限り、ログファイルへの書き込みであり、ユーザーに表示することさえできません。

いずれにせよ、プレゼンターはバックエンドでモデルを使用して何らかの作業を行い、ある時点で何が起こっているかをユーザーに通知したいと考えています。そのため、プレゼンターのどこかにビューInformUserメッセージを呼び出すメソッドがあります。

class Presenter
{
  public void DoSomething()
  {
    _view.InformUser("Starting model processing...");
  }
}

これがデカップリングを取得する場所です。プレゼンターは、IViewの実装への参照のみを保持し、実際に実装方法を気にしません。

また、ビューでプレゼンターへの参照があり、オブジェクトがコンストラクターを介して設定されるため、これも貧弱な実装です。より堅牢なソリューションでは、おそらく、Windsor、Ninjectなどの制御の反転(IoC)コンテナーを調べて、実行時にオンデマンドでIViewの実装を解決し、それをさらに分離することをお勧めします。


4

Controller / Presenterはアクションが実際に行われる場所であることを覚えておくことが重要だと思います。必要に応じて、コントローラーでの結合は避けられません。

コントローラのコア・ポイントを使用すると、ビューに変更を加える場合は、そのモデルが変更とする必要がないようであるその逆(モデルビューのいずれかに持っていない変更した場合)コントローラが変換するものであるため、ビューにモデル化し、再び元に戻します。ただし、モデルまたはビューが変更されると、コントローラーが変更されます。これは、ビューで行われた変更をモードに戻す方法をコントローラー内で効果的に変換する必要があるためです。

私ができる最良の例は、MVCアプリを作成するとき、GUIビューにデータがあるだけでなく、モデルからプルされたデータをstringデバッガーに表示するためにプッシュするルーチンを作成することもできることです。(および拡張子によりプレーンテキストファイルに)。ビューまたはモデルおよびコントローラーのみを変更せずに、モデルデータを取得してテキストに自由に変換できる場合、正しい道を歩んでいます。

そうは言っても、すべてのコンポーネントを機能させるには、異なるコンポーネント間の参照が必要になります。コントローラは、データをプッシュするためにビューについて知る必要があり、ビューは、変更が行われたとき(ユーザーが「保存」または「新規...」をクリックしたときなど)を知らせるためにコントローラについて知る必要があります。コントローラーはデータを引き出すためにモデルについて知る必要がありますが、モデルは他のことを知るべきではないと主張します。

警告:私は完全にMac、Objective-C、Cocoaのバックグラウンドを持っているので、あなたが本当に望むかどうかにかかわらず、MVCパラダイムにあなたを押し込みます。


これは間違いなく私の目標です。私の主な問題は、Viewを設定する方法です-各ダイアログのインスタンスを持つクラスでなければならず、Dialog.Gettersを呼び出すView.Gettersを使用するか、プレゼンターがDialog.Gettersを直接呼び出すことができるかどうか(これはあまりにも密に結合しているように見えるので、おそらくそうではないでしょうか?)
trycatch

プレゼンター/コントローラーはビューに対して完全に責任を負うべきだと思います。繰り返しますが、いくつかのカップリングは必ず発生しますが、少なくとも責任の方向が明確であれば、長期的にはメンテナンスが容易になります。
フィリップレーガン

2
私は確かにP / CがViewに責任を負うべきであることに同意しますが、MVPを強力にするはずの部分はUIライブラリ全体を引き出し、新しいものをプラグインする能力とマッサージ(dllimportingなど)でしたその場所で別のものを実行できるようにします。Controller / Presenterがダイアログに直接アクセスすることで、これはより困難になりませんか?私は確かに議論しようとしているのではなく、さらに理解してください:)
trycatch

本当の力は2つの方向から来ていると思います。1つ目は、ビューとモデルが他とは何の関係もないこと、2つ目は、開発作業の大部分であるアプリのエンジンがきちんと含まれていることです。ユニット、コントローラー。しかし、責任のある程度の出血は必ず発生します。少なくともインターフェースのスワッピングの大部分はコントローラーで行われ、ビューからのリンクは最小限になります。他の人が言ったように、ロジックの出血が予想され、許可されています。MVCは魔法の弾丸ではありません。
フィリップレーガン

デカップリングの重要なポイントは、プレゼンターが明確に定義されたインターフェイス(UIライブラリに依存しない)を介してのみビューにアクセスすることです。そのため、UIライブラリを別のインターフェイス(フォーム/ウィンドウ/ダイアログ/ページに同じインターフェイスを実装する別のインターフェイス)に置き換えることができます/制御/何でも)
マーセル・トス

2

一般的に、モデルにはそのモデルとのすべての相互作用をカプセル化する必要があります。たとえば、CRUDアクション(作成、読み取り、更新、削除)はすべてモデルの一部です。特別な計算についても同じことが言えます。これには、いくつかの正当な理由があります。

  • このコードのテストを自動化する方が簡単です
  • 重要なものをすべて1か所に保管します

コントローラー(MVCアプリ)で行うことは、ビューで使用する必要があるモデルを収集し、そのモデルで適切な関数を呼び出すことだけです。モデルの状態に対する変更は、このレイヤーで発生します。

ビューには、準備したモデルが表示されるだけです。基本的に、ビューはモデルのみを読み取り、それに応じて出力を調整します。

一般原則を実際のクラスにマッピングする

ダイアログはビューであることを忘れないでください。既にダイアログクラスがある場合、別の「表示」クラスを作成する理由はありません。Presenterレイヤーは、基本的にモデルをビュー内のコントロールにバインドします。ビジネスロジックとすべての重要なデータはモデルに保存されます。

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