web-dev-qa-db-fra.com

Que contient le "contrôleur" dans "MVC"?

Je pense comprendre les concepts de base de MVC: le modèle contient les données et le comportement de l'application, la vue est chargée de l'afficher à l'utilisateur et le contrôleur prend en charge les entrées de l'utilisateur. Ce dont je ne suis pas sûr, c'est exactement quoi va dans le contrôleur.

Disons par exemple que j'ai une application assez simple (je pense spécifiquement à Java, mais je suppose que les mêmes principes s'appliquent ailleurs). J'organise mon code en 3 paquets appelés app.model, app.view Et app.controller.

Dans le package app.model, J'ai quelques classes qui reflètent le comportement réel de l'application. Ces extends Observable Et utilisent setChanged() et notifyObservers() pour déclencher la mise à jour des vues, le cas échéant.

Le package app.view A une classe (ou plusieurs classes pour différents types d'affichage) qui utilise des composants javax.swing Pour gérer l'affichage. Certains de ces composants doivent être renvoyés dans le modèle. Si je comprends bien, la vue ne devrait avoir aucun rapport avec les commentaires - cela devrait être traité par le contrôleur.

Alors qu'est-ce que je mets réellement dans le contrôleur? Est-ce que je mets la public void actionPerformed(ActionEvent e) dans la vue en appelant simplement une méthode dans le contrôleur? Si tel est le cas, une validation, etc. doit-elle être effectuée dans le contrôleur? Si tel est le cas, comment puis-je renvoyer les messages d'erreur à la vue - cela doit-il transiter par le modèle ou le contrôleur doit-il simplement l'envoyer directement à la vue?

Si la validation est effectuée dans la vue, que dois-je mettre dans le contrôleur?

Désolé pour la longue question, je voulais juste documenter ma compréhension du processus et j'espère que quelqu'un pourra clarifier ce problème pour moi!

177
Paul Walker

Dans l'exemple que vous avez suggéré, vous avez raison: "l'utilisateur a cliqué sur le bouton" Supprimer cet élément "" dans l'interface devrait simplement appeler la fonction "Supprimer" du contrôleur. Cependant, le contrôleur n'a aucune idée de ce à quoi ressemble la vue. Votre vue doit donc collecter des informations telles que "quel élément a été cliqué?"

Sous forme de conversation:

Afficher: "Hé, contrôleur, l'utilisateur vient de me dire qu'il souhaite que l'élément 4 soit supprimé."
Controller: "Hmm, après avoir vérifié ses informations d'identification, il est autorisé à le faire ... Hé, modèle, je veux que vous obteniez le point 4 et faites ce que vous faites pour le supprimer . "
Model: "Le point 4 ... l'a eu. Il est supprimé. Retour à vous, contrôleur."
Controller: "Ici, je vais collecter le nouvel ensemble de données. Revenez à vous, regardez."
View: "Cool, je vais montrer le nouveau jeu à l'utilisateur maintenant."

A la fin de cette section, vous avez une option: soit la vue peut faire une requête séparée, "donnez-moi le dernier jeu de données", et donc plus pure, ou le contrôleur renvoie implicitement le nouveau jeu de données avec le message "delete " opération.

487
Andres Jaan Tack

Le problème avec MVC est que les gens pensent que la vue, le contrôleur et le modèle doivent être aussi indépendants que possible les uns des autres. Ils ne le font pas - une vue et un contrôleur sont souvent liés - pensez plutôt à M(VC).

Le contrôleur est le mécanisme de saisie de l'interface utilisateur, qui est souvent emmêlé dans la vue, en particulier avec les interfaces graphiques. Néanmoins, la vue est sortie et le contrôleur est entré. Une vue peut souvent fonctionner sans contrôleur correspondant, mais un contrôleur est généralement beaucoup moins utile sans vue. Les contrôleurs conviviaux utilisent la vue pour interpréter les entrées de l'utilisateur de manière plus significative et intuitive. C’est ce qui rend difficile de séparer le concept de contrôleur de la vue.

Imaginez un robot radiocommandé placé sur un champ de détection dans une boîte scellée.

Le modèle est axé sur les transitions d'état et d'état sans concept de sortie (affichage) ni sur ce qui déclenche les transitions d'état. Je peux obtenir la position du robot sur le terrain et le robot sait comment passer d'une position à l'autre (faire un pas en avant/en arrière/à gauche/à droite. Facile à imaginer sans vue ni contrôleur, mais ne fait rien d'utile

Pensez à une vue sans contrôleur, par exemple quelqu'un dans une autre pièce du réseau dans une autre pièce surveille la position du robot en coordonnées (x, y) en streaming sur une console avec défilement. Cette vue affiche simplement l'état du modèle, mais ce type n'a pas de contrôleur. Encore une fois, il est facile d’envisager cette vue sans contrôleur.

Pensez à un contrôleur sans vue, par exemple une personne enfermée dans un placard avec le contrôleur radio réglé sur la fréquence du robot. Ce contrôleur envoie des entrées et provoque des transitions d’état sans aucune idée de ce qu’il fait au modèle (le cas échéant). Facile à envisager, mais pas vraiment utile sans une sorte de retour de la vue.

La plupart des interfaces utilisateur conviviales coordonnent la vue avec le contrôleur pour fournir une interface utilisateur plus intuitive. Par exemple, imaginez une vue/contrôleur avec un écran tactile indiquant la position actuelle du robot en 2D et permettant à l'utilisateur de toucher le point de l'écran qui se trouve juste devant le robot. Le contrôleur a besoin de détails sur la vue, par exemple la position et l’échelle de la fenêtre, et la position de pixel du point touché par rapport à la position de pixel du robot sur l’écran) pour l’interpréter correctement (contrairement au mec enfermé dans le placard avec le contrôleur radio).

Ai-je déjà répondu à votre question? :-)

Le contrôleur est tout élément qui prend une entrée de l'utilisateur qui est utilisé pour provoquer le passage du modèle à l'état de transition. Essayez de garder la vue et le contrôleur séparés, mais sachez qu'ils sont souvent interdépendants. Il est donc normal que la frontière entre eux soit floue. En d'autres termes, avoir la vue et le contrôleur dans des packages distincts risque de ne pas être séparés de manière aussi nette que vous le feriez. comme, mais ça va. Vous devrez peut-être accepter que le contrôleur ne soit pas correctement séparé de la vue, car celle-ci provient du modèle.

... faut-il effectuer une validation, etc. dans le contrôleur? Si tel est le cas, comment puis-je renvoyer les messages d'erreur à la vue - cela doit-il transiter par le modèle ou le contrôleur doit-il simplement l'envoyer directement à la vue?

Si la validation est effectuée dans la vue, que dois-je mettre dans le contrôleur?

Je dis qu'une vue liée et un contrôleur doivent interagir librement sans passer par le modèle. Le contrôleur prend les entrées de l'utilisateur et doit effectuer la validation (peut-être en utilisant les informations du modèle et/ou de la vue), mais en cas d'échec de la validation, le contrôleur devrait pouvoir mettre à jour directement la vue associée (par exemple, un message d'erreur).

Le test à effectuer est de vous demander si un point de vue indépendant (par exemple, le gars de l’autre pièce surveillant la position du robot via le réseau) devrait voir quoi que ce soit résultant de l’erreur de validation de quelqu'un d’autre (par exemple, le gars dans le placard). essayé de dire au robot de quitter le terrain). En règle générale, la réponse est non - l'erreur de validation a empêché la transition d'état. S'il n'y a pas eu de transition d'état (le robot ne s'est pas déplacé), il n'est pas nécessaire de le dire aux autres vues. Le gars dans le placard n'a tout simplement pas eu le sentiment qu'il avait tenté de provoquer une transition illégale (aucune vue - interface utilisateur défectueuse), et personne d'autre n'a besoin de le savoir.

Si le gars avec l'écran tactile essayait d'envoyer le robot hors du terrain, il recevait un message convivial pour Nice lui demandant de ne pas tuer le robot en l'envoyant hors du champ de détection, mais encore une fois, personne d'autre n'a besoin de le savoir.

Si d'autres vues do ont besoin de connaître ces erreurs, vous indiquez en fait que les entrées de l'utilisateur et les erreurs qui en résultent sont partie du modèle et le tout est un peu plus compliqué ...

64
Bert F

Voici un bon article sur les bases de MVC.

Il est dit ...

Contrôleur - Le contrôleur traduit les interactions avec la vue en actions à exécuter par le modèle.

En d'autres termes, votre logique métier. Le contrôleur répond aux actions de l'utilisateur prises dans la vue et répond. Vous mettez la validation ici et sélectionnez la vue appropriée si la validation échoue ou réussit (page d'erreur, boîte de message, etc.).

Il y a un autre bien article chez Fowler .

23
JP Alioto

Le modèle MVC veut simplement que vous séparer la présentation (= vue) de la logique métier (= modèle). La partie contrôleur n’est là que pour semer la confusion.

16
Dimitri C.

Sur la base de votre question, j'ai l'impression que vous êtes un peu flou sur le rôle du modèle. Le modèle est fixé sur les données associées à l'application; si l'application dispose d'une base de données, le modèle aura pour tâche de lui parler. Il gérera également toute logique simple associée à ces données; si vous avez une règle qui dit que dans tous les cas où TABLE.foo == "Hourra!" et TABLE.bar == "Huzzah!" puis définissez TABLE.field = "W00t!", vous voulez que le modèle en prenne soin.

Le contrôleur est ce qui devrait gérer l’essentiel du comportement de l’application. Donc pour répondre à vos questions:

"Est-ce que je mets le vide public actionPerformed (ActionEvent e) dans la vue en appelant simplement une méthode dans le contrôleur?"

Je dirais non. Je dirais que cela devrait vivre dans le contrôleur; La vue doit simplement alimenter les données provenant de l'interface utilisateur dans le contrôleur et laisser le contrôleur décider des méthodes à appeler en réponse.

"Si tel est le cas, une validation, etc. doit-elle être effectuée dans le contrôleur?"

La majeure partie de votre validation devrait en réalité être effectuée par le contrôleur; il doit répondre à la question de savoir si les données sont valides ou non. Si ce n'est pas le cas, envoyez les messages d'erreur appropriés à la vue. En pratique, vous pouvez incorporer des contrôles de cohérence simples dans la couche View afin d'améliorer l'expérience de l'utilisateur. (Je pense principalement aux environnements Web, où vous voudrez peut-être qu'un message d'erreur apparaisse dès que l'utilisateur cliquera sur "Soumettre" plutôt que d'attendre le processus de soumission -> processus -> cycle de chargement de la page avant de leur dire qu'ils ont foiré. .) Fait attention; vous ne voulez pas dupliquer vos efforts plus que nécessaire, et dans beaucoup d'environnements (encore une fois, je pense au Web), vous devez souvent traiter les données provenant de l'interface utilisateur comme un paquet d'ordures sales mensonges jusqu'à ce que vous avez confirmé que c'est vraiment légitime.

"Si tel est le cas, comment puis-je renvoyer les messages d'erreur à View - ceux-ci doivent-ils être renvoyés dans le modèle ou le contrôleur doit-il les renvoyer directement à View?"

Vous devriez avoir un protocole configuré pour que la vue ne sache pas nécessairement ce qui se passera jusqu'à ce que le contrôleur l'ait indiqué. Quel écran leur montrez-vous après que l'utilisateur ait appuyé sur ce bouton? Il se peut que la vue ne le sache pas et que le contrôleur ne le sache pas non plus tant qu'il n'a pas examiné les données qu'il vient de recevoir. Cela peut être "Allez sur cet autre écran, comme prévu" ou "Restez sur cet écran et affichez ce message d'erreur".

D'après mon expérience, la communication directe entre le modèle et la vue devrait être très très limitée et la vue ne devrait modifier directement aucune des données du modèle; cela devrait être le travail du contrôleur.

"Si la validation est effectuée dans la vue, que dois-je mettre dans le contrôleur?"

Voir au dessus; la validation réelle devrait être dans le contrôleur. Et j'espère que vous avez une idée de ce qui devrait être mis dans le contrôleur maintenant. :-)

Il convient de noter que tout cela peut devenir un peu flou sur les bords; comme dans la plupart des domaines aussi complexes que l'ingénierie logicielle, les jugements abondent. Utilisez simplement votre meilleur jugement, essayez de rester cohérent au sein de cette application et essayez d'appliquer les leçons que vous avez apprises au prochain projet.

9
BlairHippo

En pratique, je n'ai jamais trouvé le concept de contrôleur particulièrement utile. J'utilise une séparation stricte modèle/vue dans mon code, mais il n'y a pas de contrôleur clairement défini. Cela semble être une abstraction inutile.

Personnellement, le MVC à part entière semble être le modèle de conception d'usine en ce sens qu'il conduit facilement à une conception déroutante et trop compliquée. Ne soyez pas un astronaute en architecture .

9
John Kugelman

Le contrôleur fait vraiment partie de la vue. Son travail consiste à déterminer le (s) service (s) nécessaire (s) pour répondre à la demande, à dissocier les valeurs de la vue en objets requis par l'interface de service, à déterminer la vue suivante et à réorganiser la réponse dans un formulaire utilisable par la vue suivante. . Il gère également toutes les exceptions générées et les restitue dans des vues que les utilisateurs peuvent comprendre.

La couche de service est la chose qui connaît les cas d'utilisation, les unités de travail et les objets de modèle. Le contrôleur sera différent pour chaque type de vue - vous n'aurez pas le même contrôleur pour les interfaces utilisateur de bureau, basées sur un navigateur, Flex ou mobile. Donc, je dis que cela fait vraiment partie de l'interface utilisateur.

Axé sur le service: c'est là que le travail est effectué.

7
duffymo

Le contrôleur sert principalement à la coordination entre la vue et le modèle.

Malheureusement, il finit parfois par se mêler à la vue - dans de petites applications bien que ce ne soit pas si grave.

Je vous suggère de mettre le:

public void actionPerformed(ActionEvent e)

dans le contrôleur. Ensuite, votre écouteur d’action dans votre vue devrait déléguer au contrôleur.

Quant à la partie validation, vous pouvez la mettre dans la vue ou dans le contrôleur, je pense personnellement que cela appartient au contrôleur.

Je recommanderais certainement de jeter un coup d'œil à Passive View et Supervising Presenter (qui est essentiellement ce en quoi Model View Presenter est divisé - du moins par Fowler). Voir:

http://www.martinfowler.com/eaaDev/PassiveScreen.html

http://www.martinfowler.com/eaaDev/SupervisingPresenter.html

3
Jon

Voici une règle de base que j'utilise: s'il s'agit d'une procédure que j'utiliserai spécifiquement pour une action sur this page, elle appartient au contrôleur, pas à la modèle. Le modèle ne doit fournir qu’une abstraction cohérente du stockage de données.

Je suis arrivé à cette idée après avoir travaillé avec une application Web volumineuse écrite par des développeurs qui pensaient être compris de MVC mais qui ne comprenaient vraiment pas. Leurs "contrôleurs" sont réduits à huit lignes de méthodes de classe statiques appelantes, appelées généralement nulle part ailleurs: -/rendant leurs modèles un peu plus que des moyens de créer des espaces de noms. Refactoriser correctement cela fait trois choses: décale tout le code SQL dans la couche d'accès aux données (modèle), rend le code du contrôleur un peu plus détaillé mais beaucoup plus compréhensible, et réduit à néant les anciens fichiers "modèle". :-)

3
staticsan

notez également que chaque widget Swing peut être considéré comme contenant les trois composants MVC: chacun a un modèle (par exemple ButtonModel), une vue (BasicButtonUI) et un contrôle (JButton lui-même).

1
akf

Vous avez essentiellement raison sur ce que vous mettez dans le contrôleur. C'est la seule façon dont le modèle doit interagir avec la vue. L'action réalisée peut être placé dans la vue, mais les fonctionnalités réelles peuvent être placées dans une autre classe qui jouerait le rôle de contrôleur. Si vous envisagez de le faire, je vous recommande d’examiner le modèle de commande, qui permet d’abréger toutes les commandes ayant le même récepteur. Désolé pour la digression.

Quoi qu'il en soit, une implémentation MVC appropriée comportera uniquement les interactions suivantes: Modèle -> Vue Voir -> Contrôleur Contrôleur -> Vue

Si vous utilisez un observateur pour mettre à jour la vue, le seul endroit où il peut y avoir une autre interaction est celle-ci devra demander au contrôleur les informations dont il a besoin.

1
mnuzzo

Nous le faisons ainsi, en utilisant principalement les contrôleurs pour gérer et réagir aux entrées/actions commandées par l'utilisateur (et _Logic pour tout le reste, sauf la vue, les données et les éléments évidents de _Model):

(1) (réponse, réaction - ce que l'application Web "fait" en réponse à l'utilisateur) Blog_Controller

-> main ()

-> handleSubmit_AddNewCustomer ()

-> verifyUser_HasProperAuth ()

(2) (logique "professionnelle", comment et comment "webapp" la webapp) Blog_Logic

-> sanityCheck_AddNewCustomer ()

-> handleUsernameChange ()

-> sendEmail_NotifyRequestedUpdate ()

(3) (vues, portails, comment la webapp "apparaît") Blog_View

-> genWelcome ()

-> genForm_AddNewBlogEntry ()

-> genPage_DataEntryForm ()

(4) (objet de données uniquement, acquis dans _construct () de chaque blog * classe, utilisé pour conserver toutes les données webapp/inmemory ensemble dans un seul objet) Blog_Meta

(5) (couche de données de base, lecture/écriture dans les bases de données) Blog_Model

-> saveDataToMemcache ()

-> saveDataToMongo ()

-> saveDataToSql ()

-> loadData ()

Parfois, nous ne savons pas trop où mettre une méthode, en C ou en L. Mais le modèle est solide comme le roc, limpide, et comme toutes les données en mémoire résident dans la _Meta, c'est une évidence là aussi. . Notre plus grand bond en avant a été l'adoption de l'utilisation de _Meta, qui a effacé tout le fouillis des différents objets _C, _L et _Model, ce qui a rendu tout cela facile à gérer mentalement appelé "Injection de dépendance", ou un moyen de transmettre un environnement complet avec toutes les données (dont le bonus est la création facile d'un environnement "de test").

0
FYA

Si je comprends bien, le contrôleur traduit les actions de l'interface utilisateur en actions au niveau de l'application. Par exemple, dans un jeu vidéo, le contrôleur peut traduire "déplace la souris avec autant de pixels" en "veut regarder dans telle direction. Dans une application CRUD, la traduction peut être" cliquée sur tel ou tel bouton "pour "Imprimer cette chose", mais le concept est le même.

0
David Seiler