Инъекция MVP

с использованием MVP, каков нормальный порядок построения и инъекции зависимостей.

обычно вы создаете презентатор для каждого представления и передаете представление в презентаторе на конструкторе. Но что, если у вас есть:

  • Служба, для которой несколько просмотров должны прослушивать события.
  • Несколько представлений, указывающих на один и тот же кэш данных.

может кто-нибудь отобразить нормальный поток информации от пользователя, щелкнув на данные, возвращающиеся в службу с сервера.

Ответ 1

Вот что я делаю:

Сначала я определяю тезисы интерфейсов:

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.
  • Код установщика завершает двунаправленную ассоциацию
  • Контейнер возвращает пару Presenter/View

Он позволяет вам вводить другие зависимости (службы, репозитории) как в ваше представление, так и в ведущего. Но в описанном вами сценарии я рекомендую вам вводить службы и кеши в презентатор вместо представления.

Ответ 2

В 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).

Ответ 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;
        }
    }
}