質問
MVPを使用して、構築と依存性注入の通常の順序は何ですか。
通常、ビューごとにプレゼンターを作成し、コンストラクターでビューをプレゼンターに渡します。しかし、あなたが持っている場合:
- 複数のビューがイベントをリッスンする必要があるサービス。
- すべてが同じデータモデルキャッシュを指す複数のビュー。
ユーザーのクリックからサーバーからサービスに戻ってくるデータへの通常の情報の流れを誰かが表示できます。
解決
これが私がやることです:
最初に、これらのインターフェースを定義します:
public interface IView<TPresenter>
{
TPresenter Presenter { get; set; }
}
public interface IPresenter<TView, TPresenter>
where TView : IView<TPresenter>
where TPresenter : IPresenter<TView, TPresenter>
{
TView View { get; set; }
}
次に、この抽象プレゼンタークラス:
public abstract class AbstractPresenter<TView, TPresenter> : IPresenter<TView, TPresenter>
where TView : IView<TPresenter>
where TPresenter : class, IPresenter<TView, TPresenter>
{
protected TView view;
public TView View
{
get { return this.view; }
set
{
this.view = value;
this.view.Presenter = this as TPresenter;
}
}
}
ビューは、セッターで双方向の影響を可能にするために、コンストラクターではなくプロパティを介して注入されます。安全なキャストが必要なことに注意してください...
その後、私の具体的なプレゼンターは次のようなものです:
public class MyPresenter : AbstractPresenter<IMyView, MyPresenter>
{
//...
}
IMyView
が IView
を実装する場所。具象ビュータイプが存在する必要があります(例: MyView
)が、それを解決するのはコンテナです:
- 一時的な動作で、
MyPresenter
タイプをコンテナにそれ自体として登録します。 - 一時的な振る舞いでコンテナに
MyView
をIMyView
として登録します。 - 次に、コンテナに
MyPresenter
を要求します。 - コンテナはMyViewをインスタンス化します
-
MyPresenter
をインスタンス化します
-
AbstractPresenter.View
プロパティを介してプレゼンターにビューを挿入します。 - セッターコードは双方向の関連付けを完了します
- コンテナはカップルのプレゼンター/ビューを返します
ビューとプレゼンターの両方に他の依存関係(サービス、リポジトリ)を注入できます。ただし、説明したシナリオでは、サービスとキャッシュをに注入することをお勧めします。ビューの代わりにプレゼンター。
他のヒント
WinFormsでは、単純なアプローチを好みます。通常、デザインサーフェイスでいくつかのUserControlを処理しています。これらをビュークラスにします。 .NETは(InitializeComponentを介して)コントロール階層を作成します。 パッシブビューパターンを使用する場合、各ビューはプレゼンターをインスタンス化します。 (これは、直接行うか、IOCコンテナに問い合わせることによって行うことができます。)コンストラクターインジェクションを使用して、ビューのインターフェイスへの参照をプレゼンターのコンストラクターに渡します。その後、プレゼンターは自分自身を接続してイベントを表示できます。モデルのプロセスを繰り返します。プレゼンターはモデルをインスタンス化し、そのイベントに結び付けます。 (この場合、パッシブビューでは、プレゼンターがモデルへの参照を保持しているため、その逆ではないため、コンストラクター注入は不要です。)
このアプローチで見つけた唯一の点は、モデルとプレゼンターのライフタイムを適切に管理することです。ビューをできるだけシンプルに保ちたいので、おそらくプレゼンターへの参照を維持したくないでしょう。ただし、これは、このプレゼンターオブジェクトが、ビューに関連付けられたイベントハンドラーとぶらぶらしていることを意味します。この設定により、ビューがガベージコレクションされなくなります。 1つの解決策は、閉じていることを示すイベントをビューに発行させることです。プレゼンターはイベントを受け取り、そのモデルとビューのサブスクリプションの両方を削除します。 Web内のオブジェクトは適切に逆参照され、ガベージコレクターはその作業に取りかかることができます。
次のようになります:
public interface IView
{
...
event Action SomeEvent;
event EventHandler Disposed;
...
}
// Note that the IView.Disposed event is implemented by the
// UserControl.Disposed event.
public class View : UserControl, IView
{
public event Action SomeEvent;
public View()
{
var presenter = new Presenter(this);
}
}
public interface IModel
{
...
event Action ModelChanged;
...
}
public class Model : IModel
{
...
public event Action ModelChanged;
...
}
public class Presenter
{
private IView MyView;
private IModel MyModel;
public Presenter(View view)
{
MyView = view;
MyView.SomeEvent += RespondToSomeEvent;
MyView.Disposed += ViewDisposed;
MyModel = new Model();
MyModel.ModelChanged += RespondToModelChanged;
}
// You could take this a step further by implementing IDisposable on the
// presenter and having View.Dispose() trigger Presenter.Dispose().
private void ViewDisposed(object sender, EventArgs e)
{
MyView.SomeEvent -= RespondToSomeEvent;
MyView.Disposed -= ViewDisposed;
MyView = null;
MyModel.Modelchanged -= RespondToModelChanged;
MyModel = null;
}
}
IOCを使用し、IOCコンテナにIModel(Presenterクラス)およびIPresenter(Viewクラス)の実装を要求することにより、この例をさらに分離できます。
interface IEmployee
{
int EmployeeId {get;}
string FirstName {get;}
string LastName {get;}
}
interface IEmployeeRepository
{
void SaveEmployee(IEmployee employee);
IEmployee GetEmployeeById(int employeeId);
IEmployee[] Employees { get; }
}
interface IEmployeeView
{
event Action<IEmployee> OnEmployeeSaved;
}
interface IEmployeeController
{
IEmployeeView View {get;}
IEmployeeRepository Repository {get;}
IEmployee[] Employees {get;}
}
partial class EmployeeView: UserControl, IEmployeeView
{
public EmployeeView()
{
InitComponent();
}
}
class EmployeeController:IEmployeeController
{
private IEmployeeView view;
private IEmployeeRepository repository;
public EmployeeController(IEmployeeView view, IEmployeeRepository repository)
{
this.repository = repository;
this.view = view;
this.view.OnEmployeeSaved+=new Action<IEmployee>(view_OnEmployeeSaved);
}
void view_OnEmployeeSaved(IEmployee employee)
{
repository.SaveEmployee(employee);
}
public IEmployeeView View
{
get
{
return view;
}
}
public IEmployeeRepository Repository
{
get
{
return repository;
}
}
public IEmployee[] Employees
{
get
{
return repository.Employees;
}
}
}
WinformsMVPは、Windowsフォーム用の非常に優れたMVPフレームワークです。このフレームワークを使用すると、複数のビューに簡単にサービスを簡単に注入できます。 これは良い記事ですサンプルソースコードはフレームワークの使用方法を説明しています。