Внедрение зависимостей MVP

#c# #winforms #mvp

Вопрос:

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

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

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

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

Ответ №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 ), но его решает контейнер:

  1. Я регистрирую MyPresenter тип как сам по себе в контейнере с переходным поведением.
  2. Я регистрируюсь MyView как IMyView в контейнере с переходным поведением.
  3. Затем я прошу а MyPresenter к контейнеру.
  4. Контейнер создает экземпляр MyView
  5. Это создает экземпляр MyPresenter
  6. Он вводит представление в презентатора через AbstractPresenter.View свойство.
  7. Код задатчика завершает двунаправленную ассоциацию
  8. Контейнер возвращает презентер/Представление пары

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

Комментарии:

1. Как вы справляетесь с сомнительными проблемами? В частности, нарушать циклические ссылки, чтобы разрешить сбор мусора?

Ответ №2:

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

Ответ №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-это очень хороший MVP-фреймворк для Windows forms. Вы можете легко внедрить сервис в несколько представлений, используя эту платформу. Это хорошая статья с образцом исходного кода, в которой объясняется, как использовать фреймворк.