web-dev-qa-db-ja.com

モデル-ビュー-プレゼンター実装の考え

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

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

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

私はしようとしています:

  1. これを可能な限り分離する
  2. 理想的には、プレゼンター/モデルを他の言語のビューと組み合わせることができるようにします(私はたくさんの言語間機能を実行していませんが、特にvoid(void)にこだわることができることはわかっています。少なくともC++ライブラリを備えたC#アプリ...
  3. コードをクリーンでシンプルに保つ

それで、相互作用がどのように処理されるべきかについての提案はありますか?

34
trycatch

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

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

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

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

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

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

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

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

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トライアド間の通信ですが、この答えはすでに十分に長くなっています。

37
Kenneth Cochran

皆が言ったように、意見は数十あり、どれも正しいか間違っているというわけではありません。無数のパターンに触れずに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の実装への参照のみを保持し、実装方法を実際には気にしません。

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

8
Bil Simser

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

コントローラのコアポイントは、ビューに変更を加えた場合、モデルを変更する必要がなく、逆も同様です(モデルを変更した場合、ビューも変更する必要はありません)。なぜなら、コントローラはモデルをビューに変換し、また戻すものだからです。ただし、モデル内で行われた変更をモードに戻す方法をモデル内でどのように表示するかをコントローラー内で効果的に変換する必要があるため、モデルまたはビューが変更されると、コントローラーが変更されます。

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

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

警告:私は、Mac、Objective-C、Cocoaの完全なバックグラウンドを持っているので、やりたいかどうかに関係なく、MVCパラダイムに引き込まれます。

4
Philip Regan

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

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

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

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

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

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

2
Berin Loritsch