Вопрос

используя 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 для контейнера.
  • Контейнер создает экземпляр MyView
  • Он создает MyPresenter
  • Он внедряет представление в презентатор через свойство AbstractPresenter.View .
  • Код установщика завершает двунаправленную связь
  • Контейнер возвращает пару Presenter / 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. Вы можете легко внедрить службу в несколько представлений, используя эту платформу. Это хорошая статья с пример исходного кода объясняет, как использовать платформу.

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top