используя 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 для контейнера.MyPresenterAbstractPresenter.View.Это позволяет вам внедрять другие зависимости (службы, репозитории) как в ваше представление, так и в вашего докладчика. Но в описанном вами сценарии я рекомендую вам внедрять службы и кеши в ведущий вместо представления.
В WinForms я предпочитаю простой подход. Обычно вы имеете дело с несколькими UserControls на поверхности дизайна - сделайте их своими классами представления. .NET создает для вас иерархию элементов управления (через InitializeComponent). Если вы используете шаблон Пассивный вид, каждое представление затем создает экземпляр своего презентатора. (Вы можете сделать это напрямую или запросив контейнер IOC.) Используйте внедрение конструктора, чтобы передать ссылку на интерфейс представления конструктору докладчика. Затем ведущий может подключиться к просмотру событий. Повторите процесс для модели: докладчик создает экземпляр модели и подключается к ее событиям. (В этом случае вам не нужна инъекция конструктора, поскольку в пассивном представлении докладчик сохраняет ссылку на модель, а не наоборот.)
Единственная проблема, которую я обнаружил в этом подходе, - это правильное управление сроками службы модели и ведущего. Вы хотите, чтобы представление было как можно более простым, поэтому вы, вероятно, не хотите, чтобы оно сохраняло ссылку на докладчика. Однако это означает, что у вас есть этот объект-презентатор, завязанный с обработчиками событий, привязанными к вашему представлению. Эта настройка предотвращает сборку мусора для вашего представления. Одно из решений - сделать так, чтобы ваше представление публиковало событие, указывающее на его закрытие. Ведущий получит событие и удалит подписки на модель и представление. Теперь объекты в вашей сети разыменованы должным образом, и сборщик мусора может продолжить свою работу.
Вы получаете что-то вроде следующего:
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 - очень хороший фреймворк MVP для форм Windows. Вы можете легко внедрить сервис в несколько представлений, используя эту структуру. Этот - хорошая статья с образцом исходного кода, объясняющая, как использовать фреймворк.
Как вы справляетесь с проблемами IDisposable? В частности, нарушить циклические ссылки, чтобы разрешить сборку мусора?