Вопрос
используя 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
для контейнера. MyPresenter
AbstractPresenter.View
. Он позволяет вам внедрять другие зависимости (сервисы, репозитории) как в ваше представление, так и в докладчика. Но в описанном вами сценарии я рекомендую вам внедрять сервисы и кэши в докладчик вместо представления.
Другие советы
В WinForms я предпочитаю простой подход. Обычно вы имеете дело с несколькими пользовательскими элементами управления на поверхности проектирования - сделайте их своими классами представления. .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. Вы можете легко внедрить службу в несколько представлений, используя эту платформу. Это хорошая статья с пример исходного кода объясняет, как использовать платформу.