抽象化は良いですが、ある時点で何かが1つか2つのことを知っている必要があることを覚えておくことが重要です。そうしないと、うまく抽象化されたレゴの山が組み立てられるのではなく、床に置かれることになります。家。
制御の反転/依存性注入/flippy-dippy-upside-down-whatever-私たちが今週呼んでいるものは何でも、Autofacのようなコンテナはこれをすべてまとめるのに本当に役立ちます。
WinFormsアプリケーションをまとめると、通常、繰り返しパターンが発生します。
Program.cs
Autofacコンテナーを構成するファイルから始めて、MainForm
そこからインスタンスをフェッチし、を表示しMainForm
ます。これをシェル、ワークスペース、またはデスクトップと呼ぶ人もいますが、いずれにせよ、メニューバーがあり、子ウィンドウまたは子ユーザーコントロールのいずれかを表示する「フォーム」であり、閉じるとアプリケーションが終了します。
次は前述MainForm
です。Visual Studioビジュアルデザイナーでドラッグアンドドロップなどの基本的な作業を行ってからSplitContainers
、MenuBar
コードに夢中になり始めます。特定のキーインターフェイスをMainForm
コンストラクターに「挿入」して、それらを利用できるので、私のMainFormは、それらについてそれほど多くを知る必要なしに、子コントロールを調整できます。
たとえば、さまざまなコンポーネントがまたはのような「イベント」を公開またはサブスクライブできるようにするインターフェイスIEventBroker
があるとします。これにより、アプリケーションの一部は、従来の.NETイベントの配線に依存することなく、緩く結合された方法でイベントに応答できます。たとえば、それは私の言うことができ、そのイベントのサブスクライバーのリストをチェックして、コールバックを呼び出します。たとえば、はそのイベントをリッスンし、動的に更新することができますBarcodeScanned
ProductSaved
EditProductPresenter
EditProductUserControl
this.eventBroker.Fire("ProductSaved", new EventArgs<Product>(blah))
IEventBroker
ListProductsPresenter
ListProductsUserControl
それが取り付けられていること。最終的な結果として、ユーザーが1つのユーザーコントロールに製品を保存すると、別のユーザーコントロールのプレゼンターは、コントロールがお互いの存在を認識したりMainForm
、オーケストレーションしたりすることなく、開いている場合に反応して更新できます。そのイベント。
MDIアプリケーションを設計している場合は、メソッドを持つインターフェイスをMainForm
実装している可能性があります。そのインターフェイスをさまざまなプレゼンターに挿入して、プレゼンターが直接気付かないうちに追加のウィンドウを開いたり閉じたりできるようにすることができます。たとえば、ユーザーがのデータグリッドの行をダブルクリックしたときに、に対応するを開きたい場合があります。これは、実際にはを参照できますが、それを知る必要はありません。呼び出して、コントロールがアプリケーションの適切な場所に表示されていると想定します。(実装は、おそらく、コントロールをどこかのパネルのビューにスワップします。)IWindowWorkspace
Open()
Close()
MainForm
ListProductsPresenter
EditProductPresenter
EditProductUserControl
ListProductsUserControl
IWindowWorkspace
MainForm
Open(newInstanceOfAnEditControl)
MainForm
しかし、どうやってそのインスタンスをListProductsPresenter
作成するEditProductUserControl
のでしょうか?Autofacのデリゲートファクトリは、プレゼンターにデリゲートを挿入するだけで、Autofacがファクトリーであるかのように自動的に接続するため、ここでは真の喜びです(擬似コードは次のとおりです)。
public class EditProductUserControl : UserControl
{
public EditProductUserControl(EditProductPresenter presenter)
{
// initialize databindings based on properties of the presenter
}
}
public class EditProductPresenter
{
// Autofac will do some magic when it sees this injected anywhere
public delegate EditProductPresenter Factory(int productId);
public EditProductPresenter(
ISession session, // The NHibernate session reference
IEventBroker eventBroker,
int productId) // An optional product identifier
{
// do stuff....
}
public void Save()
{
// do stuff...
this.eventBroker.Publish("ProductSaved", new EventArgs(this.product));
}
}
public class ListProductsPresenter
{
private IEventBroker eventBroker;
private EditProductsPresenter.Factory factory;
private IWindowWorkspace workspace;
public ListProductsPresenter(
IEventBroker eventBroker,
EditProductsPresenter.Factory factory,
IWindowWorkspace workspace)
{
this.eventBroker = eventBroker;
this.factory = factory;
this.workspace = workspace;
this.eventBroker.Subscribe("ProductSaved", this.WhenProductSaved);
}
public void WhenDataGridRowDoubleClicked(int productId)
{
var editPresenter = this.factory(productId);
var editControl = new EditProductUserControl(editPresenter);
this.workspace.Open(editControl);
}
public void WhenProductSaved(object sender, EventArgs e)
{
// refresh the data grid, etc.
}
}
したがって、機能セット(つまり、編集プレゼンターと編集ユーザーコントロール)ListProductsPresenter
について知っています。これは完全に問題ありません。これらは密接に関連していますが、のすべての依存関係についてEdit
知る必要はありません。機能セット。代わりに、Autofacが提供するデリゲートに依存して、それらの依存関係をすべて解決します。Edit
一般的に、私は「プレゼンター/ビューモデル/監視コントローラー」(結局のところ、それらはすべて非常に似ているので、違いにあまり追いつかないようにしましょう)と「UserControl
/ Form
"。はUserControl
、コンストラクターでプレゼンター/ビューモデル/コントローラーを受け入れ、必要に応じてそれ自体をデータバインドし、可能な限りプレゼンターに延期します。一部の人々はUserControl
、のようなインターフェースを介してプレゼンターからを非表示にしますIEditProductView
。これは、ビューが完全にパッシブでない場合に役立ちます。私はすべてにデータバインディングを使用する傾向があるので、通信は経由で行われ、INotifyPropertyChanged
気になりません。
しかし、プレゼンターが恥知らずにビューに結び付けられている場合、あなたはあなたの人生をはるかに楽にするでしょう。オブジェクトモデルのプロパティがデータバインディングとメッシュされていませんか?新しいプロパティを公開します。EditProductPresenter
とを1つのレイアウトで使用することは決してなくEditProductUserControl
、同じプレゼンターで機能する新しいバージョンのユーザーコントロールを作成する必要があります。両方を編集するだけです。これらはすべての目的と目的のために1つのユニット、1つの機能であり、プレゼンターはユニットのテストが容易で、ユーザーコントロールがないため、存在するだけです。
機能を置き換え可能にする場合は、機能全体をそのように抽象化する必要があります。だからあなたはあなたが話しかけるINavigationFeature
インターフェースを持っているかもしれません。を実装し、によって消費されるをMainForm
持つことができます。また、を実装し、によって消費されるものがあるかもしれません。ユーザーコントロールとプレゼンターは引き続き連携しますが、ツリーベースのビューとカルーセルベースのビューのどちらとやり取りするかを気にする必要はなく、賢くなくてもそれらを交換できます。TreeBasedNavigationPresenter
INavigationFeature
TreeBasedUserControl
CarouselBasedNavigationPresenter
INavigationFeature
CarouselBasedUserControl
MainForm
MainForm
最後に、混乱しがちです。誰もが衒学者であり、わずかに異なる用語を使用して、類似したアーキテクチャパターン間の微妙な(そして多くの場合重要ではない)違いを伝えます。私の謙虚な意見では、依存性注入は、結合が抑えられているため、構成可能で拡張可能なアプリケーションを構築するのに不思議に思います。機能を「プレゼンター/ビューモデル/コントローラー」と「ビュー/ユーザーコントロール/フォーム」に分離すると、ほとんどのロジックが前者に引き込まれ、ユニットテストが容易になるため、品質に驚かされます。そして、2つの原則を組み合わせることは本当にあなたが探しているもののようです、あなたはただ用語で混乱しているだけです。
または、私はそれでいっぱいになる可能性があります。幸運を!