Selon la description de MVP par Martin Fowler ( http://martinfowler.com/eaaDev/uiArchs.html )
À propos de la partie View de MVC, Fowler dit:
Le premier élément de Potel est de traiter la vue comme une structure de widgets, des widgets qui correspondent aux contrôles du modèle Forms and Controls et de supprimer toute séparation vue / contrôleur. La vue de MVP est une structure de ces widgets. Il ne contient aucun comportement décrivant comment les widgets réagissent à l'interaction de l'utilisateur .
(Mine d'accentuation en gras)
Puis du présentateur:
La réaction active aux actes de l'utilisateur vit dans un objet présentateur distinct. Les gestionnaires fondamentaux pour les gestes des utilisateurs existent toujours dans les widgets, mais ces gestionnaires ne font que passer le contrôle au présentateur .
Le présentateur décide ensuite comment réagir à l'événement. Potel aborde cette interaction principalement en termes d'actions sur le modèle, ce qu'il fait par un système de commandes et de sélections. Une chose utile à souligner ici est l'approche de l'empaquetage de toutes les modifications du modèle dans une commande - cela fournit une bonne base pour fournir un comportement d'annulation / rétablissement.
(Encore une fois, la mienne est en gras)
Ainsi, conformément aux directives de Fowler, votre vue ne devrait être responsable d'aucun comportement en réponse à l'événement de bouton; qui comprend la création d'une instance de UserInfo
. La responsabilité de décider de créer un objet appartient à la méthode Presenter à laquelle l'événement d'interface utilisateur est transmis.
Cependant, on pourrait également affirmer que le gestionnaire d'événements de bouton de la vue ne devrait pas non plus être responsable de la transmission du contenu de votre textView
, car la vue devrait simplement transmettre l'événement de bouton au présentateur et rien de plus.
Avec MVP, il est courant que la vue implémente une interface que le présentateur peut utiliser pour récupérer des données directement à partir de la vue (tout en s'assurant que le présentateur est toujours indépendant de la vue elle-même). Étant donné que UserInfo est un simple POJO, il peut être valide pour la vue d'exposer un getter pour UserInfo que le présentateur peut récupérer depuis la vue via une interface.
// 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 quoi est-ce différent de passer UserInfo
directement dans la vue à l'aide du gestionnaire d'événements? La principale différence est que le présentateur est toujours en fin de compte responsable de la logique qui provoque la UserInfo
création d' un objet. c'est-à-dire que l'événement a atteint le présentateur avant la création du UserInfo
, permettant au présentateur de prendre la décision.
Imaginez un scénario où vous aviez une logique de présentateur où vous ne vouliez pas que cela UserInfo
soit créé en fonction d'un état dans la vue. Par exemple, si l'utilisateur n'a pas coché une case dans la vue, ou si vous avez effectué une vérification de validation par rapport à un champ à ajouter dans UserInfo qui a échoué - votre présentateur peut contenir une vérification supplémentaire avant d'appeler GetUserInfo
- c'est-à-dire
private boolean IsUsernameValid() {
String username = addUserView.GetUsername();
return (username != null && !username.isEmpty());
}
public void onSomethingClicked() {
if (IsUsernameValid()) {
UserInfo userInfo = addUserView.GetUserInfo();
// etc.
}
}
Cette logique reste à l'intérieur du présentateur et n'a pas besoin d'être ajoutée à la vue. Si la vue était responsable de l'appel, GetUserInfo()
elle serait également responsable de toute logique entourant son utilisation; c'est ce que le modèle MVP essaie d'éviter.
Ainsi, alors que la méthode qui crée qui UserInfo
peut physiquement exister dans la classe View, elle n'est jamais appelée à partir de la classe View, uniquement à partir du Presenter.
Bien sûr, si la création des UserInfo
fichiers finit par nécessiter des vérifications supplémentaires par rapport au contenu des widgets d'entrée utilisateur (par exemple, conversion de chaîne, validation, etc.), il serait préférable d'exposer des getters individuels pour ces choses afin que la validation / conversion de chaîne puisse prendre placer dans le présentateur - puis le présentateur crée votre UserInfo
.
Dans l'ensemble, votre objectif principal en ce qui concerne la séparation entre Presenter / View est de vous assurer que vous n'avez jamais besoin d'écrire de logique dans la vue. Si jamais vous avez besoin d'ajouter une if
déclaration pour une raison quelconque (même s'il s'agit d'une if
déclaration concernant l'état d'une propriété de widget - vérifier une zone de texte vide ou un booléen pour une case à cocher), alors elle appartient au présentateur.
onSomethingClicked()
, donc lorsque l'utilisateur clique sur "quelque chose", la vue appellepresenter.onSomethingClicked()
? Ou mes méthodes de présentation devraient être nommées comme les actions prévues, dans mon casaddUser()
?Presenter
est bien sûr responsable de la logique d'interface utilisateur plutôt que de la logique de domaine, et spécialement adapté à laView
, donc les concepts qui devraient exister sont des concepts d'interface utilisateur, donc une méthode nomméeonSomethingClicked()
est en effet appropriée. Avec le recul, la dénomination que j'ai choisie dans mon exemple ci-dessus ne sent pas très bien :-).GetUserInfo
méthode dans la vue comme vous l'avez mentionné (sera déclenchée par le présentateur) Qu'en est-il desif
conditions possibles à l' intérieur de laGetUserInfo
méthode? Peut-être que certains champs de UserInfo seront définis via la réaction de l'utilisateur? Un scénario: peut - être que l'utilisateur sélectionne une case à cocher, puis certains nouveaux composants (un nouveau EditText peut-être) seront visibles par l'utilisateur. Donc, dans ce cas, laGetUserInfo
méthode aura une condition if. Dans ce scénarioGetUserInfo
est toujours valide?UserInfo
comme un modèle de la vue (alias "Afficher le modèle") - Dans ce scénario, j'ajouterais l'boolean
état de la case à cocher et l'état vide / annulableString
de la zone de texteUserInfo
. Vous pourriez même envisager de le renommerUserInfoViewModel
si cela aide à penser en termes de POJO étant une classe dont le seul véritable objectif est de laisser lesUserInfoPresenter
informations sur l'état de la vue.