Реализация идеи Модель-Представление-Презентатор

34

Я пытаюсь получить хорошее представление о том, как реализовать хорошее разделение между пользовательским интерфейсом и моделью, но мне сложно понять, где именно разделить линии.

Я смотрел на Model-View-Presenter, но я не уверен, как именно реализовать его. Например, мой вид имеет несколько диалогов.

  • Должен ли быть класс View с экземплярами каждого из диалогов? Тогда в таком случае, как диалоги должны взаимодействовать с докладчиком? то есть. если отдельное диалоговое окно должно запрашивать данные из модели через докладчик, как диалоговое окно должно получить ссылку на докладчик? Через ссылку на вид, данный ему во время строительства?
  • Я думал, может быть, представление должно быть статическим классом? Тогда диалоги GetView и получить оттуда Presenter ...
  • Я думал о настройке Presenter с правами на View и Model (в отличие от View с Presenter и Presenter с Model) и Presenter, регистрирующих обратные вызовы для событий в View, но из-за этого это выглядит много более связанный (или язык зависит, по крайней мере.)

Я пытаюсь:

  1. сделать это как можно отделенным
  2. в идеале сделать возможным связать Presenter / Model с представлениями других языков (я не делал тонны межъязыковых вещей, но я знаю, что это возможно, особенно, тем более void(void)я могу придерживаться, по крайней мере, приложения C # с Библиотека C ++ ...
  3. сохранить код в чистоте и простоте

Итак ... какие-либо предложения, как взаимодействия должны быть обработаны?

попробуй поймать
источник
Вы смотрели на эту статью ?: en.wikipedia.org/wiki/Model-view-presenter
Бернард
1
У меня есть .. Я нашел это немного быстро и на высоком уровне, я хочу лучше понять, как обрабатывать несколько диалогов в большом проекте с как можно
меньшей

Ответы:

37

Добро пожаловать на скользкий склон. К этому моменту вы поняли, что существует бесконечное разнообразие всех взаимодействий модели и вида. MVC, MVP (Taligent, Dolphin, Passive View), MVVM - это лишь некоторые из них.

Шаблон Model View Presenter, как и большинство архитектурных шаблонов, открыт для большого разнообразия и экспериментов. Единственное, что объединяет все вариации, это роль презентатора как посредника между представлением и моделью. Двумя наиболее распространенными являются пассивный просмотр и ведущий ведущий / контроллер - [ Фаулер ]. Пассивное представление рассматривает пользовательский интерфейс как очень поверхностный интерфейс между пользователем и докладчиком. В нем содержится очень мало логики, если она делегирует столько же ответственности докладчику. Супервайзер Ведущий / Контролерпытается использовать преимущества привязки данных, встроенной во многие инфраструктуры пользовательского интерфейса. Пользовательский интерфейс выполняет синхронизацию данных, но докладчик / контроллер вмешивается для более сложной логики. В любом случае модель, вид и презентатор образуют триаду

Есть много способов сделать это. Очень часто это можно увидеть, рассматривая каждый диалог / форму как отдельное представление. Часто между представлениями и докладчиками есть соотношение 1: 1. Это не сложное, быстрое правило. Довольно часто один докладчик обрабатывает несколько связанных представлений или наоборот. Все зависит от сложности представления и сложности бизнес-логики.

Что касается того, как представления и докладчики получают ссылку друг на друга, это иногда называют проводкой . У вас есть три варианта:

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

MyForm.SomeEvent(Sender)
{
  Presenter.DoSomething(Sender.Data);
}

Поскольку докладчик не имеет ссылки на представление, представление должно отправлять ему данные в качестве аргументов. Докладчик может связаться с представлением, используя функции событий / обратного вызова, которые представление должно прослушивать.

Presenter содержит ссылку на представление.
В сценарии представление предоставляет свойства для данных, которые оно отображает пользователю. Докладчик прослушивает события и манипулирует свойствами в представлении:

Presenter.SomeEvent(Sender)
{
  DomainObject.DoSomething(View.SomeProperty);
  View.SomeOtherProperty = DomainObject.SomeData;
}

Оба содержат ссылку друг на друга, образуя круговую зависимость. С
этим сценарием на самом деле легче работать, чем с другими. Представление реагирует на события, вызывая методы в презентаторе. Докладчик считывает / изменяет данные из представления через открытые свойства.

View.SomeEvent(Sender)
{
  Presenter.DoSomething();
}

Presenter.DoSomething()
{
  View.SomeProperty = DomainObject.Calc(View.SomeProperty);
}

Есть и другие проблемы, которые следует учитывать при использовании шаблонов MVP. Порядок создания, время жизни объекта, где происходит подключение, связь между триадами MVP, но этот ответ уже достаточно вырос.

Кеннет Кокран
источник
1
Это определенно полезно. Связь между триадами и жизнью - вот где у меня сейчас проблемы, когда я кое-что понимаю.
trycatch
8

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

Держите их отдельно. Представление должно реализовывать интерфейс, который формирует связь между представлением и презентатором. Представление создает презентатора и внедряет себя в презентатора и предоставляет методы, которые оно предлагает для презентатора для взаимодействия с представлением. Представление отвечает за реализацию этих методов или свойств любым удобным для них способом. Как правило, у вас есть одно представление: один докладчик, но в некоторых случаях вы можете иметь несколько представлений: один докладчик (веб, wpf и т. Д.). Ключевым моментом здесь является то, что докладчик ничего не знает о реализации пользовательского интерфейса и взаимодействует только с представлением через интерфейс.

Вот пример. Сначала у нас есть класс представления с простым методом для отображения сообщения пользователю:

interface IView
{
  public void InformUser(string message);
}

Теперь вот ведущий. Обратите внимание, что докладчик принимает IView в свой конструктор.

class Presenter
{
  private IView _view;
  public Presenter(IView view)
  {
    _view = view;
  }
}

Теперь вот фактический пользовательский интерфейс. Это может быть окно, диалог, веб-страница и т. Д. Не имеет значения. Обратите внимание, что конструктор для представления создаст презентатора, внедрив себя в него.

class View : IView
{
  private Presenter _presenter;

  public View()
  {
    _presenter = new Presenter(this);
  }

  public void InformUser(string message)
  {
    MessageBox.Show(message);
  }
}

Докладчик не заботится о том, как представление реализует метод, который он просто делает. Насколько известно докладчику, это может быть запись в файл журнала и даже не показ его пользователю.

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

class Presenter
{
  public void DoSomething()
  {
    _view.InformUser("Starting model processing...");
  }
}

Это где вы получаете свою развязку. Докладчик содержит только ссылку на реализацию IView, и ему все равно, как он реализован.

Это также плохая реализация, так как у вас есть ссылка на Presenter в представлении, а объекты задаются с помощью конструкторов. В более надежном решении вы, вероятно, захотите взглянуть на контейнеры инверсии управления (IoC), такие как Windsor, Ninject и т. Д., Которые позволят вам реализовать реализацию IView во время выполнения по запросу и, таким образом, сделать ее еще более отделенной.

Бил Симсер
источник
4

Я думаю, что важно помнить, что Controller / Presenter - это место, где действие действительно происходит. Сцепление в контроллере неизбежно из-за необходимости.

Суть Контроллера в том, что если вы вносите изменения в Представление, тогда Модель не должна изменяться, и наоборот (если Модель меняет Представление, это тоже не обязательно), потому что Контроллер - это то, что переводит Модель в представление и обратно. Но Контроллер изменится, когда изменятся либо Модель, либо Представление, потому что вы фактически должны преобразовать в Контроллере то, как Модель должна Просматривать, как вернуть изменения, сделанные в Представлении, обратно в Режим.

Лучший пример, который я могу привести, это то, что когда я пишу приложение MVC, я могу не только иметь данные в представлении GUI, но и написать подпрограмму, которая помещает данные, извлеченные из модели, в объект stringдля отображения в отладчике. (и, соответственно, в виде простого текстового файла). Если я могу взять данные модели и свободно перевести их в текст, не меняя представление или модель и только контроллер, то я на правильном пути.

При этом вам нужно будет иметь ссылки между различными компонентами, чтобы все это работало. Контроллеру необходимо знать о представлении для передачи данных, а представление должно знать о контроллере, чтобы сообщить ему, когда было внесено изменение (например, когда пользователь нажимает «Сохранить» или «Новый ...»). Контроллер должен знать о модели, чтобы получить данные, но я бы сказал, что модель не должна знать ни о чем другом.

Предостережение: Я пришел из Mac, Objective-C, из Какао, который действительно подталкивает вас к парадигме MVC, хотите вы этого или нет.

Филип Реган
источник
Это определенно моя цель. Моя основная проблема заключается в том, как настроить View - должен ли он быть классом с экземпляром каждого диалога, а затем использовать View.Getters, которые вызывают Dialog.Getters, или если Presenter должен иметь возможность вызывать Dialog.Getters напрямую ( это кажется слишком тесно связанным, так что, вероятно, нет?)
trycatch
Я думаю, что Ведущий / Контролер должен нести полную ответственность за Представления, поэтому последнее. Опять же, некоторая связь обязательно должна произойти, но, по крайней мере, если направление ответственности ясно, то обслуживание должно быть легче в долгосрочной перспективе.
Филипп Риган
2
Я, конечно, согласен с тем, что P / C должен нести ответственность за View, но я подумал, что частью того, что должно было сделать MVP мощным, была возможность вытащить всю библиотеку пользовательского интерфейса и подключить новую с некоторым массажем (dllimporting и еще много чего) быть в состоянии запустить еще один на своем месте. Разве это не было бы сложнее, если бы Controller / Presenter напрямую обращался к диалогам? Я уж точно не пытаюсь спорить, просто дальше разбираюсь :)
trycatch
Я думаю, что реальная сила исходит от двух направлений: во-первых, вид и модель не имеют никакого отношения к другим, и во-вторых, большинство разработок, движка приложения, выполняется аккуратно блок, контроллер. Но должно произойти некоторое кровотечение ответственности. По крайней мере, большая часть обмена интерфейсами будет выполняться в контроллере, и любые ссылки из представления будут минимальными. Как уже говорили другие, некоторое кровопролитие ожидается и допустимо. MVC не волшебная пуля.
Филипп Риган,
Важным моментом для развязки является то, что презентатор ТОЛЬКО получает доступ к представлению через четко определенные интерфейсы (независимые от библиотеки UI), поэтому библиотека UI может быть заменена на другую (другую, которая будет реализовывать тот же интерфейс для формы / окна / диалога / страницы). / контроль / что угодно)
Марсель Тот
2

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

  • Проще автоматизировать тестирование для этого кода
  • Он хранит все эти важные вещи в одном месте

В вашем контроллере (приложение MVC) все, что вы делаете, - это собираете модели, которые вы должны использовать в своем представлении, и вызываете соответствующие функции в модели. Любые изменения в состоянии модели происходят в этом слое.

Ваш вид просто отображает модели, которые вы подготовили. По сути, представление только читает модель и соответствующим образом корректирует ее вывод.

Отображение общего принципа на реальные классы

Помните, что ваши диалоги являются представлениями. Если у вас уже есть класс диалога, нет причин создавать другой класс «View». Слой Presenter по существу связывает модель с элементами управления в представлении. Бизнес-логика и все важные данные хранятся в модели.

Берин Лорич
источник