问题
using MVP, what is the normal order of construction and dependency injection.
normally you create a presenter for each view and pass the view into the presenter on constructor. But what if you have:
- A Service that multiple views need to listen to events on.
- Multiple views all pointing to the same data model cache.
can someone display a normal flow of info from a user click to data coming back in a service from a server.
回答1:
Here is what I do:
First, I define theses interfaces:
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; }
}
Then this abstract presenter class:
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;
}
}
}
The view is injected via a property, instead of the constructor, to allow the bi-directional affection in the setter. Notice that a safe cast is needed...
Then, my concrete presenter is something like :
public class MyPresenter : AbstractPresenter<IMyView, MyPresenter>
{
//...
}
Where IMyView
implements IView
. A concrete view type must exists (e.g. MyView
), but it's the container that resolves it:
- I register
MyPresenter
type as itself in the container, with a transient behavior. - I register
MyView
as anIMyView
in the container with a transient behavior. - I then asks for a
MyPresenter
to the container. - Container instanciate a MyView
- It instanciates a
MyPresenter
- It inject the view into the presenter through the
AbstractPresenter.View
property. - The setter code completes the bi-directional association
- The container returns the couple Presenter/View
It allows you to inject other dependencies (services, repos) into both your view and your presenter. But in the scenario you described, I recommend you to inject services and caches into the presenter, instead of the view.
回答2:
In WinForms, I prefer a simple approach. Usually you're dealing with a few UserControls on a design surface -- make these your view classes. .NET creates the control hierarchy for you (via InitializeComponent). If you use the Passive View pattern, each view then instantiates it's presenter. (You can do this either directly or by asking an IOC container.) Use constructor injection to pass a reference to the view's interface to the presenter's constructor. The presenter can then wire itself up to view events. Repeat the process for the model: the presenter instantiates a model and wires up to its events. (In this case you don't need the constructor injection since Passive View says the presenter keeps a reference to the model, not vice versa.)
The only nit I've found with this approach is properly managing lifetimes of the model and presenter. You want to keep the view as simple as possible, so you probably don't want it maintaining a reference to the presenter. However, that means you've got this presenter object hanging around with event handlers tied to your view. This setup prevents your view from being garbage collected. One solution is to have your view publish an event that indicates it's closing. The presenter would receive the event and remove both its model and view subscriptions. The objects in your web are now properly dereferenced and the garbage collector can go about its work.
You wind up with something like the following:
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;
}
}
You can decouple this example a step further by using IOC and asking your IOC container for implementations of IModel (in the Presenter class) and IPresenter (in the View class).
回答3:
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;
}
}
}
回答4:
WinformsMVP is a very good MVP framework for Windows forms. You can easily inject an service across multiple views easily using this framework. This is a good article with a sample source code explains how to use the framework.
来源:https://stackoverflow.com/questions/189121/mvp-dependency-injection