Согласно описанию Мартина Фаулера о MVP ( http://martinfowler.com/eaaDev/uiArchs.html )
Фаулер говорит о части MVC «Вид»:
Первым элементом Potel является обработка представления как структуры виджетов, виджетов, которые соответствуют элементам управления модели Forms и Controls, и удаление любого разделения представления / контроллера. Представление MVP является структурой этих виджетов. Он не содержит поведения, которое описывает, как виджеты реагируют на взаимодействие с пользователем .
(Жирный упор мой)
Затем от ведущего:
Активная реакция на действия пользователя живет в отдельном объекте презентатора. Фундаментальные обработчики пользовательских жестов все еще существуют в виджетах, но эти обработчики просто передают управление докладчику .
Затем докладчик решает, как реагировать на событие. Потель обсуждает это взаимодействие в первую очередь с точки зрения действий над моделью, которые он выполняет с помощью системы команд и выборов. Здесь полезно отметить подход, заключающийся в упаковке всех правок модели в команду - это обеспечивает хорошую основу для обеспечения поведения отмены / повтора.
(Опять жирный акцент мой)
Таким образом, в соответствии с рекомендациями Фаулера, ваш Просмотр не должен нести ответственность за любое поведение в ответ на событие кнопки; который включает в себя создание экземпляра UserInfo
. Ответственность за принятие решения о создании объекта принадлежит методу Presenter, которому перенаправляется событие пользовательского интерфейса.
Однако можно также утверждать, что обработчик события кнопки View также не должен отвечать за передачу содержимого вашего элемента textView
, поскольку View должен просто пересылать событие кнопки в Presenter и ничего более.
В MVP для представления обычно реализован интерфейс, который может использовать докладчик для получения данных непосредственно из представления (при этом гарантируя, что докладчик по-прежнему не зависит от самого представления). Так как UserInfo является простым POJO, это может быть справедливо для вида , чтобы выставить геттер для UserInfo которой ведущий может забрать Фрон View через интерфейс.
// The view would implement IView
public interface IView {
public UserInfo GetUserInfo();
}
// Presenter
public class AddUserPresenter {
private IView addUserView;
public void SetView(IView view) {
addUserView = view
}
public void onSomethingClicked() {
UserInfo userInfo = addUserView.GetUserInfo();
// etc.
}
}
Чем это отличается от передачи UserInfo
непосредственно в представление с помощью обработчика событий? Основное отличие состоит в том, что докладчик по-прежнему несет конечную ответственность за логику, которая вызывает создание UserInfo
объекта. то есть событие достигло докладчика до его создания UserInfo
, что позволяет докладчику принять решение.
Представьте себе сценарий, в котором у вас была логика презентатора, в которой вы не хотели, UserInfo
чтобы она создавалась на основе некоторого состояния в представлении. Например, если пользователь не установил флажок в представлении или вы проверили проверку по некоторому полю, которое было добавлено в UserInfo, но не удалось - ваш докладчик может содержать дополнительную проверку перед вызовом GetUserInfo
- т.е.
private boolean IsUsernameValid() {
String username = addUserView.GetUsername();
return (username != null && !username.isEmpty());
}
public void onSomethingClicked() {
if (IsUsernameValid()) {
UserInfo userInfo = addUserView.GetUserInfo();
// etc.
}
}
Эта логика остается внутри презентатора, и ее не нужно добавлять в представление. Если бы представление отвечало за вызов, GetUserInfo()
то оно также отвечало бы за любую логику, окружающую его использование; это то, что пытается избежать шаблон MVP.
Таким образом, хотя метод, который создает, который UserInfo
может физически существовать в классе View, он никогда не вызывается из класса View, только из Presenter.
Конечно, если создание в UserInfo
конечном итоге требует дополнительных проверок содержимого пользовательских виджетов ввода (например, преобразование строк, проверка и т. Д.), То было бы лучше выставить отдельные средства получения для этих вещей, чтобы проверка / преобразование строк могло занять место внутри докладчика - и тогда докладчик создает свой UserInfo
.
В целом, вашей главной целью в отношении разделения между Presenter / View является обеспечение того, чтобы вам никогда не приходилось писать логику в представлении. Если вам когда-либо понадобится добавить if
оператор по какой-либо причине (даже если это if
оператор относительно состояния свойства виджета - проверка пустого текстового поля или логическое значение для флажка), то он принадлежит представителю.
onSomethingClicked()
, чтобы, когда пользователь нажимает «что-то», вызывается Viewpresenter.onSomethingClicked()
? Или мои методы презентатора должны быть названы как предполагаемые действия, в моем случаеaddUser()
?Presenter
Это, конечно , отвечает за логику пользовательского интерфейса , а не логики домена, и с учетом конкретно кView
, поэтому понятия , которые должны существовать такие понятия UI, поэтому метод названonSomethingClicked()
действительно является целесообразным. Оглядываясь назад, названия, которые я выбрал в моем примере выше, не совсем пахнут :-).GetUserInfo
метод в представлении, как вы упомянули (будет вызван докладчиком). Как насчет возможныхif
условий внутриGetUserInfo
метода? Может быть, некоторые поля UserInfo будут установлены через реакцию пользователя? Сценарий: возможно, пользователь установит флажок, после чего некоторые новые компоненты (возможно, новый EditText) будут видны пользователю. Так что в этом случаеGetUserInfo
метод будет иметь условие if. В этом сценарииGetUserInfo
действует еще?UserInfo
как модель представления (иначе «Модель представления») - в этом сценарии я бы добавил в негоboolean
состояние флажка и состояние пустого / пустого значенияString
для текстового поляUserInfo
. Вы можете даже подумать о его переименовании,UserInfoViewModel
если это поможет понять, что POJO - это класс, единственная реальная цель которого - датьUserInfoPresenter
информацию о состоянии View.