UIとモデル間の適切なデカップリングを実装する方法を十分に理解しようとしていますが、行を分割する場所を正確に把握するのに問題があります。
私はModel-View-Presenterを見てきましたが、それをどのように実装するか正確にはわかりません。たとえば、私のビューには複数のダイアログがあります。
私はしようとしています:
void(void)
にこだわることができることはわかっています。少なくともC++ライブラリを備えたC#アプリ...それで、相互作用がどのように処理されるべきかについての提案はありますか?
滑りやすい斜面へようこそ。この時点で、すべてのモデルとビューの相互作用には無限のバリエーションがあることがわかりました。 MVC、MVP(Taligent、Dolphin、Passive View)、MVVMはほんの数例です。
ほとんどの建築パターンと同様に、モデルビュープレゼンターパターンは、多くの多様性と実験に開放されています。すべてのバリエーションに共通しているのは、ビューとモデルの間の「仲介者」としてのプレゼンターの役割です。最も一般的な2つは、Passive ViewとSupervising 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トライアド間の通信ですが、この答えはすでに十分に長くなっています。
皆が言ったように、意見は数十あり、どれも正しいか間違っているというわけではありません。無数のパターンに触れずに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の実装を解決し、それによってさらに分離することができます。
コントローラー/プレゼンターはアクションが実際に行われる場所であることを覚えておくことは重要だと思います。必要なため、コントローラでの結合は避けられません。
コントローラのコアポイントは、ビューに変更を加えた場合、モデルを変更する必要がなく、逆も同様です(モデルを変更した場合、ビューも変更する必要はありません)。なぜなら、コントローラはモデルをビューに変換し、また戻すものだからです。ただし、モデル内で行われた変更をモードに戻す方法をモデル内でどのように表示するかをコントローラー内で効果的に変換する必要があるため、モデルまたはビューが変更されると、コントローラーが変更されます。
私が与えることができる最良の例は、MVCアプリを作成するときに、GUIビューにデータを表示できるだけでなく、モデルからプルされたデータをstring
にプッシュするルーチンを作成できることです。デバッガーに表示されます(プレーンテキストファイルへの拡張によって)。モデルデータを取得して、ビューまたはモデルとonlyコントローラを変更せずに自由にテキストに変換できる場合、私は正しい道にいます。
そうは言っても、すべてを機能させるには、異なるコンポーネント間の参照が必要になります。コントローラーはデータをプッシュするビューについて知る必要があり、ビューは変更について(ユーザーが「保存」または「新規...」をクリックするときなど)コントローラーに通知する必要があります。コントローラはデータをプルするためにモデルについて知っている必要がありますが、モデルは他のことについて知ってはいけないと私は主張します。
警告:私は、Mac、Objective-C、Cocoaの完全なバックグラウンドを持っているので、やりたいかどうかに関係なく、MVCパラダイムに引き込まれます。
一般に、モデルにはそのモデルとのすべての相互作用をカプセル化する必要があります。たとえば、CRUDアクション(作成、読み取り、更新、削除)はすべてモデルの一部です。特別な計算についても同様です。これには2つの理由があります。
コントローラー(MVCアプリ)では、ビューで使用する必要があるモデルを収集し、モデルの適切な関数を呼び出すだけです。モデルの状態の変更は、このレイヤーで発生します。
ビューには、準備したモデルが表示されます。基本的に、ビューはモデルを読み取るだけで、それに応じて出力を調整します。
一般的な原則を実際のクラスにマッピングする
ダイアログはビューであることに注意してください。すでにダイアログクラスがある場合は、別の「ビュー」クラスを作成する理由はありません。 Presenterレイヤーは基本的に、モデルをビューのコントロールにバインドします。ビジネスロジックとすべての重要なデータはモデルに格納されます。