Secondo la descrizione di Martin Fowler su MVP ( link )
Della porzione View di MVC, Fowler dice:
The first element of Potel is to treat the view as a structure of widgets, widgets that correspond to the controls of the Forms and Controls model and remove any view/controller separation. The view of MVP is a structure of these widgets. It doesn't contain any behavior that describes how the widgets react to user interaction.
(Enfasi grassetto)
Quindi del presentatore:
The active reaction to user acts lives in a separate presenter object. The fundamental handlers for user gestures still exist in the widgets, but these handlers merely pass control to the presenter.
The presenter then decides how to react to the event. Potel discusses this interaction primarily in terms of actions on the model, which it does by a system of commands and selections. A useful thing to highlight here is the approach of packaging all the edits to the model in a command - this provides a good foundation for providing undo/redo behavior.
(Di nuovo, enfasi sfacciata)
Quindi, in conformità con le linee guida di Fowler, il tuo View non dovrebbe essere responsabile di alcun comportamento in risposta all'evento del pulsante; che include la creazione di un'istanza di UserInfo
. La responsabilità di decidere di creare un oggetto appartiene al metodo Presenter a cui viene inoltrato l'evento UI.
Tuttavia, si potrebbe anche obiettare che anche il gestore di eventi del pulsante View non dovrebbe essere responsabile della trasmissione del contenuto di textView
, poiché la vista dovrebbe semplicemente essere forwarding l'evento button nel Presentatore e niente di più.
Con MVP, è comune per la vista implementare un'interfaccia che il relatore può utilizzare per raccogliere i dati direttamente dalla vista (assicurandosi che il relatore sia ancora agnostico alla vista stessa). Dato che UserInfo è un semplice POJO, potrebbe essere valido per la vista esporre un getter per UserInfo che il Presenter può raccogliere dalla Vista tramite un'interfaccia.
// 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.
}
}
In che modo è diverso passare il UserInfo
direttamente alla vista usando il gestore di eventi? La differenza principale è che il relatore è ancora in definitiva responsabile della logica che causa la creazione di un oggetto UserInfo
. Ad esempio, l'evento ha raggiunto il presentatore prima della creazione del UserInfo
, consentendo al Presenter di prendere la decisione.
Immagina uno scenario in cui hai una logica presenter in cui non desideri che UserInfo
venga creato in base a uno stato all'interno della visualizzazione. Ad esempio, se l'utente non ha spuntato una casella di controllo sulla vista, o se si è verificato un controllo di convalida su alcuni campi da aggiungere a UserInfo che non è riuscito, il relatore potrebbe contenere un ulteriore controllo prima di chiamare GetUserInfo
- i.e.
private boolean IsUsernameValid() {
String username = addUserView.GetUsername();
return (username != null && !username.isEmpty());
}
public void onSomethingClicked() {
if (IsUsernameValid()) {
UserInfo userInfo = addUserView.GetUserInfo();
// etc.
}
}
Questa logica rimane all'interno del presentatore e non è necessario aggiungerla alla vista. Se la vista fosse responsabile della chiamata di GetUserInfo()
, sarebbe anche responsabile di qualsiasi logica che circonda il suo utilizzo; che è ciò che il pattern MVP sta cercando di evitare.
Quindi, mentre il metodo che crea quel UserInfo
può esistere fisicamente nella classe View, non viene mai chiamato dalla classe View, solo dal Presenter.
Naturalmente, se la creazione di UserInfo
finisce per richiedere ulteriori controlli sul contenuto dei widget di input dell'utente (es. conversione di stringhe, convalida, ecc.), allora sarebbe meglio esporre i singoli getter per quelle cose in modo che la convalida / conversione delle stringhe può aver luogo all'interno del Presenter - e quindi il relatore crea il UserInfo
.
Nel complesso, il tuo obiettivo principale in merito alla separazione tra Presenter / View è quello di garantire che mai non sia necessario scrivere la logica nella vista. Se ti capita di dover aggiungere un'istruzione if
per qualsiasi motivo (anche se si tratta di un'istruzione if
riguardante lo stato di una proprietà del widget - controllo di una casella di testo vuota o di un booleano per una casella di controllo), allora appartiene a il presentatore.