De acuerdo con la descripción de MVP de Martin Fowler ( http://martinfowler.com/eaaDev/uiArchs.html )
De la parte de Vista de MVC, Fowler dice:
El primer elemento de Potel es tratar la vista como una estructura de widgets, widgets que corresponden a los controles del modelo de Formularios y controles y eliminar cualquier separación de vista / controlador. La vista de MVP es una estructura de estos widgets. No contiene ningún comportamiento que describa cómo reaccionan los widgets a la interacción del usuario .
(Énfasis en negrita mío)
Luego del presentador:
La reacción activa a los actos del usuario vive en un objeto presentador separado. Los controladores fundamentales para los gestos del usuario todavía existen en los widgets, pero estos controladores simplemente pasan el control al presentador .
El presentador luego decide cómo reaccionar ante el evento. Potel discute esta interacción principalmente en términos de acciones en el modelo, lo que hace mediante un sistema de comandos y selecciones. Una cosa útil para resaltar aquí es el enfoque de empaquetar todas las ediciones al modelo en un comando; esto proporciona una buena base para proporcionar un comportamiento de deshacer / rehacer.
(Nuevamente, el énfasis en negrita es mío)
Por lo tanto, de acuerdo con las pautas de Fowler, su Vista no debe ser responsable de ningún comportamiento en respuesta al evento del botón; que incluye crear una instancia de UserInfo
. La responsabilidad de decidir crear un objeto pertenece al método Presentador al que se reenvía el evento de IU.
Sin embargo, uno también podría argumentar que el controlador de eventos del botón Ver tampoco debería ser responsable de pasar el contenido de su textView
tampoco, ya que la Vista simplemente debe reenviar el evento del botón al Presentador y nada más.
Con MVP, es común que la vista implemente una interfaz que el presentador puede usar para recoger datos directamente de la vista (a la vez que se asegura de que el presentador aún sea independiente de la vista en sí). Dado que UserInfo es un POJO simple, puede ser válido para la vista exponer un captador para UserInfo que el presentador puede recoger desde la vista a través de una interfaz.
// 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.
}
}
¿En qué se diferencia esto de pasar UserInfo
directamente a la vista usando el controlador de eventos? La principal diferencia es que el presentador sigue siendo en última instancia responsable de la lógica que hace UserInfo
que se cree un objeto. es decir, el evento llegó al presentador antes de la creación del UserInfo
, permitiendo que el presentador tome la decisión.
Imagine un escenario en el que tuviera una lógica de presentador en la que no quisiera que UserInfo
se creara en función de algún estado dentro de la vista. Por ejemplo, si el usuario no ha marcado una casilla de verificación en la vista, o si tuvo una verificación de validación contra algún campo para agregar a UserInfo que falló, su presentador puede contener una verificación adicional antes de llamar GetUserInfo
, es decir
private boolean IsUsernameValid() {
String username = addUserView.GetUsername();
return (username != null && !username.isEmpty());
}
public void onSomethingClicked() {
if (IsUsernameValid()) {
UserInfo userInfo = addUserView.GetUserInfo();
// etc.
}
}
Esa lógica permanece dentro del presentador y no necesita agregarse a la vista. Si la vista fuera responsable de llamar GetUserInfo()
, también sería responsable de cualquier lógica que rodeara su uso; que es lo que el patrón MVP está tratando de evitar.
Entonces, aunque el método que crea que UserInfo
puede existir físicamente en la clase View, nunca se llama desde la clase View, solo desde el Presentador.
Por supuesto, si la creación de los UserInfo
extremos requiere verificaciones adicionales contra el contenido de los widgets de entrada del usuario (por ejemplo, conversión de cadenas, validación, etc.), entonces sería mejor exponer captadores individuales para esas cosas para que la validación / conversión de cadenas pueda tomar colocar dentro del presentador, y luego el presentador crea su UserInfo
.
En general, su objetivo principal con respecto a la separación entre Presentador / Vista es asegurarse de que nunca necesite escribir lógica en la vista. Si alguna vez necesita agregar una if
declaración por algún motivo (incluso si se trata de una if
declaración con respecto al estado de una propiedad de widget, marcando un cuadro de texto vacío o un booleano para una casilla de verificación), entonces pertenece al presentador.
onSomethingClicked()
, por lo que cuando el usuario hace clic en "algo", la vista llamapresenter.onSomethingClicked()
? ¿O mis métodos de presentador deben ser nombrados como las acciones previstas, en mi casoaddUser()
?Presenter
supuesto, es responsable de la lógica de la interfaz de usuario en lugar de la lógica de dominio, y se adapta específicamente a laView
, por lo tanto, los conceptos que deberían existir son conceptos de la interfaz de usuario, por lo que un método llamadoonSomethingClicked()
es realmente apropiado. En retrospectiva, los nombres que he elegido en mi ejemplo anterior no huelen bien :-).GetUserInfo
método en la vista como mencionaste (se activará desde el presentador) ¿Qué pasa con las posiblesif
condiciones dentro delGetUserInfo
método? ¿Quizás algunos campos de UserInfo se establecerán mediante la reacción del usuario? Un escenario: tal vez el usuario seleccione una casilla de verificación y luego algunos componentes nuevos (tal vez un nuevo EditText) serán visibles para el usuario. Entonces, en ese caso, elGetUserInfo
método tendrá la condición if. En este escenarioGetUserInfo
es válido todavía?UserInfo
como un modelo de vista (también conocido como "Ver modelo"): en ese escenario, agregaría elboolean
estado de la casilla de verificación y el estado vacío / anulableString
del cuadro de textoUserInfo
. Incluso podría considerar cambiarle el nombreUserInfoViewModel
si eso ayuda a pensar en términos de que POJO es una clase cuyo único propósito real es permitir queUserInfoPresenter
descubra información sobre el estado Ver.