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

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

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

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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
13
0
5 733
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

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

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

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. Контейнер возвращает пару Presenter / View

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

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

aboy021 29.04.2013 06:48

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

Другие вопросы по теме