Le 'C' dans MVC est-il vraiment nécessaire?

38

Je comprends le rôle du modèle et de la vue dans le modèle Model-View-Controller, mais j'ai du mal à comprendre pourquoi un contrôleur est nécessaire.

Supposons que nous créons un programme d'échecs utilisant une approche MVC; l'état du jeu devrait être le modèle et l'interface graphique devrait être la vue. Quel est exactement le contrôleur dans ce cas?

Est-ce juste une classe séparée qui a toutes les fonctions qui seront appelées quand vous, par exemple, cliquez sur une tuile? Pourquoi ne pas simplement exécuter toute la logique du modèle dans la vue elle-même?

Anne Nonimus
la source
1
Personnellement, c'est ce que je fais . Il peut y avoir des cas où il n'y a pas d'alternative à MVC, mais je ne peux pas le supporter.
Mike Dunlavey
10
Trois mots ... "Séparation d'inquiétude".
Travis J
4
Presque tous les programmes Windows antérieurs à .net utilisaient Doc-View sans contrôleur. Cela semble avoir été relativement réussi.
Martin Beckett
Martin, monolites capables (de changer).
Indépendante
J'ai répondu ci-dessous, mais j'ajouterai que, oui, vous pouvez créer une application sans classes de contrôleur distinctes, mais ce ne serait pas MVC. Vous adoptez "une approche MVC", alors oui, les contrôleurs jouent un rôle important. Si vous choisissez un paradigme qui n'est pas MVC, il est fort possible que vous n'ayez pas de contrôleurs.
Caleb

Réponses:

4

En utilisant votre exemple, le contrôleur serait ce qui a décidé ce qui était un déménagement légal ou non. Le contrôleur indiquerait à la vue comment organiser les pièces sur le tableau au démarrage en utilisant les informations reçues du modèle. Le contrôleur peut prendre en charge davantage d'éléments, mais l'essentiel est de penser à la logique métier sur cette couche.

Il arrive parfois que le contrôleur ne fasse que passer des informations, comme une page d'inscription. D'autres fois, le contrôleur constitue la partie la plus difficile du développement car de nombreuses tâches doivent être effectuées au niveau de cette couche, telles que l'application de règles ou le calcul compliqué, par exemple. N'oubliez pas le contrôleur!

JustinDoesWork
la source
36
"En utilisant votre exemple, le contrôleur serait ce qui a décidé ce qui était un déménagement légal ou non". C'est un mauvais exemple :( une telle logique doit également figurer dans le modèle. Sinon, votre logique est partagée entre le contrôleur et le modèle.
Dime
6
@Dime - Le modèle ne doit contenir aucune logique. Le contrôleur sera la seule logique de maintien, d'où le "contrôle".
Travis J
34
@ TravisJ Pas tout à fait d'accord là-bas. Contrôler ne signifie pas savoir faire un travail. Il s'agit de contrôler les objets qui le font. Par conséquent, la logique pour effectuer le travail serait dans le modèle et le contrôleur contrôlerait le modèle à utiliser pour exécuter les exigences nécessaires de l'action, etc. À mon avis, trop de logique dans les contrôleurs serait une recette pour le contrôleur blot ...
dreza
25
L'intérêt principal de la POO est d'avoir des bits de données et un comportement cohérents maintenus ensemble et un état encapsulé en interne. "Modèle" modélise à la fois le comportement et les données.
Misko
12
-1 Le contrôleur ne doit pas contenir de logique applicative. Le modèle est "l'application", il contient les données et dispose de routines appelables pour tout ce qui peut se produire dans l'application; pas nécessairement tous dans un fichier ou une classe. La vue visualise l'état du modèle. Le contrôleur relie le modèle / vue et le monde réel / entrée. Voulez-vous "publier" l'application en tant qu'application Web? Vous avez juste besoin d’un contrôleur qui gère HTTP et d’une vue appropriée basée sur HTML. Vous voulez une interface de ligne de commande pour votre application? Juste besoin d'un contrôleur approprié et vue. Le modèle, la logique métier, ne change jamais dans ces cas.
Déceze
39

Pourquoi ne pas simplement exécuter toute la logique du modèle dans la vue elle-même?

Le contrôleur est la colle qui lie le modèle et la vue ensemble, et c'est également l'isolation qui les sépare. Le modèle ne doit rien savoir de la vue et inversement (du moins dans la version Apple de MVC). Le contrôleur agit comme un adaptateur bidirectionnel, traduisant les actions de la vue en messages au modèle et configurant la vue avec les données du modèle.

L'utilisation du contrôleur pour séparer le modèle et la vue rend votre code plus réutilisable, plus testable et plus flexible. Prenons votre exemple d'échecs. Le modèle inclurait bien sûr l’état du jeu, mais il pourrait également contenir la logique qui affecte les modifications de l’état du jeu, telles que déterminer si un déplacement est légal et décider de la fin du jeu. La vue affiche un échiquier et des pièces et envoie des messages quand une pièce bouge, mais elle ne sait rien de la signification derrière les pièces, comment chaque pièce se déplace, etc. Le contrôleur connaît à la fois le modèle et la vue, ainsi que le flux global du programme. Lorsque l'utilisateur appuie sur le bouton "nouveau jeu", il s'agit d'un contrôleur qui demande au modèle de créer un jeu et utilise l'état du nouveau jeu pour configurer le tableau. Si l'utilisateur fait un mouvement,

Regardez ce que vous obtenez en gardant le modèle et en le séparant:

  • Vous pouvez changer le modèle ou la vue sans changer l'autre. Il se peut que vous deviez mettre à jour le contrôleur lorsque vous changez l’un ou l’autre, mais cela fait partie de l’avantage: les parties du programme les plus susceptibles de changer sont concentrées dans le contrôleur.

  • Le modèle et la vue peuvent tous deux être réutilisés. Par exemple, vous pouvez utiliser la même vue de jeu d'échecs avec un flux RSS comme modèle pour illustrer des jeux célèbres. Vous pouvez également utiliser le même modèle et remplacer la vue par une interface Web.

  • Il est facile d'écrire des tests pour le modèle et la vue afin de s'assurer qu'ils fonctionnent comme ils le devraient.

  • Le modèle et la vue peuvent souvent tirer parti des éléments standard: tableaux, cartes, ensembles, chaînes et autres conteneurs de données pour le modèle; boutons, contrôles, champs de texte, vues d'image, tableaux et autres pour la vue.

Caleb
la source
1
Dans l'architecture MVC d'origine pour les applications de bureau, les vues étaient des classes actives observant directement le modèle et déconnectées du contrôleur.
Kevin Cline
Le problème avec toutes les réponses est qu'il y a autant d'interprétations de MVC que de personnes postant. Et les avantages énumérés ci-dessus ne s'appliquent qu'à une interprétation spécifique de MVC. Si l’on met la majeure partie de la logique dans l’automate (ou le modèle) et que l’appel View / initie des méthodes spécifiques sur l’automate, le combo contrôleur / modèle devient alors autonome et très réutilisable. La plupart du temps, c'est une nouvelle vue qui est nécessaire. Je n'ai jamais eu besoin de réutiliser une vue. Même votre exemple RSS pourrait être facilement géré avec une nouvelle vue qui utilise votre ancienne avec une couche RSS entre les deux.
Dunk
2
@Dunk: il est essentiel de séparer la logique métier de l'interface utilisateur pour que la logique métier puisse être testée directement.
kevin Cline
@ Kevin: Je suis tout à fait d'accord, la logique métier n'appartient pas à l'interface utilisateur. Cependant, le contrôleur n'a pas besoin de faire partie de l'interface utilisateur. C'est ce que je veux dire par beaucoup de définitions. Dans la définition d'une personne, le contrôleur gère les pressions sur les boutons, tandis qu'une autre personne l'inscrit dans le cadre de la vue. Si la vue sait comment transformer les actions de l'opérateur (par exemple, l'appui sur un bouton / la sélection d'éléments) en demandes d'application, le contrôleur / modèle devient très réutilisable avec presque tous les types d'interface utilisateur, qui peuvent inclure des interfaces réseau, une interface graphique ou une console.
Dunk
1
@Dunk: Je suppose que vous pouvez appeler tout ce que vous préférez un "contrôleur", mais dans l'architecture MVC, le contrôleur dépend de l'interface utilisateur et en fait donc partie.
Kevin Cline
7

Il existe de très nombreuses manières différentes de mettre en œuvre ce modèle de conception général, mais l’idée de base est de séparer les diverses préoccupations selon les besoins. MVC est une belle abstraction dans le sens où:

Modèle : Représentativité que les données, quoi que cela puisse signifier
Voir : représente l'interface utilisateur, ce qui pourrait signifier que
contrôleur : Représente la colle qui cause ce modèle et en vue d'Interact, quoi que cela puisse signifier

C'est extrêmement flexible car cela ne spécifie pas beaucoup. Beaucoup de gens gaspillent beaucoup de bande passante pour expliquer en détail ce que chaque élément pourrait signifier, quels noms devraient être utilisés à la place de ceux-ci, et s'il devrait y avoir réellement 3, 2, 4 ou 5 composants, mais cela manque le certain degré.

L'idée est de séparer les différents "morceaux" de la logique afin qu'ils ne se chevauchent pas. Associez vos données de présentation, vos données, vos données logiques, vos communications. Et ainsi de suite. Dans une certaine mesure, moins ces domaines de préoccupation se chevauchent, plus il est facile de faire des choses intéressantes avec eux.

C'est tout ce dont vous devriez vraiment vous soucier.

tylerl
la source
3
Colle, colle, j'aime cette définition, elle est tellement correcte: le modèle dans son ensemble devrait être baptisé MVG et les gens cesseraient de se gratter la tête à la recherche d'élégance là où il n'y en a pas.
ZJR
1
+1 pour «colle»; cela signifie également que c'est la partie la mieux adaptée pour être exécutée dans un langage de script (car ceux-ci ont tendance à exceller dans le collage).
Donal Fellows
@DonalFellows J'aime beaucoup cette idée. Quelque chose qui "colle" deux entités disparates a besoin de beaucoup de flexibilité que les langages de script faiblement typés (comme JavaScript) promeuvent
Zack Macomber
4

Toutes les bonnes réponses jusqu'à présent. Mes deux cents, c’est que j’aime penser que le contrôleur est principalement construit avec des questions comme quoi et où?

  • On m'a demandé si une pièce d'échecs (vue) pouvait être déplacée vers x. Est-ce
    permis? Je ne suis pas sûr mais je sais où et à qui demander (le modèle).
  • Quelque chose m'a demandé de sauvegarder mes données. Comment diable je fais ça? Je sais où demander cependant! Comment nous sauvegardons les données, ou l'endroit où elles sont enregistrées, je n'en ai aucune idée, mais cette classe de référentiel devrait le savoir. Je vais le transmettre et le laisser régler.
  • Je dois montrer à l'utilisateur la position actuelle de la pièce d'échecs dans laquelle le modèle l'a déplacée. Vous ne savez pas si je veux montrer la pièce en vert ou en jaune? Bah, on s'en fout, je sais qu'il y a un point de vue qui peut gérer ça, alors je leur transmettrai les données et ils pourront décider de la façon dont ils seront montrés.

Ces petits extraits sont des exemples de la façon dont j'essaie de me souvenir de l'abstraction et du concept que MVC tente de transmettre. Quoi, Où et Comment sont mes trois principaux processus de pensée.

Quoi et où => Contrôleur Comment et quand => Modèles et vues

Essentiellement, mes actions de contrôleur ont tendance à être petites et compactes et, lorsque vous les lisez, elles ont parfois l’air de perdre du temps. En les examinant de plus près, ils agissent en tant que signaleurs de la circulation, canalisant les diverses demandes vers les ouvriers appropriés mais ne faisant aucun travail proprement dit.

Dreza
la source
2

Un contrôleur peut aider à extraire les interfaces à la fois de la vue et du modèle, de sorte qu'ils n'aient pas à se connaître directement. Moins un objet doit savoir, plus il devient portable et testable d'unité.

Par exemple, le modèle pourrait jouer une autre instance d'elle-même via un contrôleur. Ou un contrôleur en réseau pourrait connecter les objets Vues de deux joueurs ensemble. Ou ce pourrait être un test de Turing où personne ne sait lequel.

hotpaw2
la source
2

Cela entre vraiment en jeu lorsque vous traitez avec des gestionnaires d'événements, mais vous avez toujours besoin du contrôleur pour gérer les interactions entre la vue et le modèle. Idéalement, vous ne voulez pas que la vue connaisse le modèle. Pensez-y, voulez-vous qu'un jsp passe directement tous les appels de base de données? (Sauf si cela ressemble à une recherche de connexion.) Vous souhaitez que la vue affiche des données sans aucune logique métier, sauf si c'est une logique de rendu de vue, mais pas une logique de gestion.

Dans GWT, vous obtenez une séparation plus nette avec MVP. Il n'y a aucune logique métier (si c'est bien fait) dans la vue. Le présentateur agit en tant que contrôleur et la vue n’a aucune connaissance du modèle. Les données de modèle sont simplement transmises à la vue.


la source
1

Document-View (modèle) est le modèle standard pour la majorité des applications Windows écrites en MFC. Il doit donc fonctionner dans de nombreux cas.

Martin Beckett
la source
1

Je comprends le rôle du modèle et de la vue dans le modèle Model-View-Controller, mais j'ai du mal à comprendre pourquoi un contrôleur est nécessaire.

Êtes-vous sûr de cela? (Du moins comme il a été décrit à l'origine) L'intérêt du modèle est d'être le modèle de domaine. La vue est censée afficher le modèle de domaine à l'utilisateur. Le contrôleur est supposé mapper les entrées de bas niveau sur le langage de modèle de haut niveau. Autant que je sache, le raisonnement s'apparente à: A) une utilisation à haut niveau du PÉR. B) Le modèle était considéré comme la partie importante de l'application, alors gardez-le sans changement et changez-le plus rapidement. C) une logique métier facilement testable (et scriptable).

Imaginez si vous voulez rendre votre programme d’échecs utilisable par les aveugles, remplacez la vue par une version audible et un contrôleur fonctionnant avec le clavier. Supposons que vous souhaitiez ajouter des jeux par courrier, ajoutez un contrôleur acceptant le texte. La version Internet du jeu? Un contrôleur qui accepte les commandes d'un socket ferait le travail. Ajoutez un joli rendu 3D, une nouvelle vue cool. Pas de changement de modèle nécessaire Les échecs sont toujours des échecs.

Si vous mélangez les entrées avec la représentation du modèle, vous perdez cette capacité. Tout à coup, les échecs ne sont pas des échecs, c’est des échecs avec une souris qui sont différents des échecs avec un clavier ou une connexion réseau.

pierre précieuse
la source
0

Je pense que MVC est idiot, peut-être que dans certains domaines, cela fonctionne bien, mais personnellement, même les sites Web que j’écris ne sont pas adaptés à MVC. Theres une raison pour laquelle vous entendez frontend, backend et jamais base de données fin ou quelque chose d'autre fin

IMO il devrait y avoir une API (backend) et l'application qui utilise l'API (frontend). J'imagine que vous pouvez appeler la requête GET le contrôleur (qui appelle simplement l'API backend) et le html la vue, mais je n'entends généralement pas les gens parler de view en tant que pur HTML ou modèle étant l'API backend.

OMI tout devrait être une API solide. En réalité, ils n'ont pas besoin d'être solides (comme dans un environnement propre et bien construit), mais ses internes doivent rester privés et l'application / frontend / en dehors de l'API ne doit jamais obtenir la connexion à la base de données, ni être capable de faire des requêtes brutes.

Maintenant, si votre code / design implique de coller sa finesse Si, dans votre jeu d’échecs, vous pouvez modifier l’habillage de l’interface graphique, l’interface graphique collecte les coords / entrées et appelle MovePiece (srcPosition, dstPostion) (qui peut renvoyer une valeur bool ou enum pour indiquer s’il s’agit d’un coup valide ou non). ) et votre ok avec toute la logique étant dans le modèle alors sûr l'appeler MVC. Cependant, j'organiserais toujours les choses par classes et API et je m'assurerais qu'aucune classe d'évier de cuisine ne touche à tout (ni aucune API à avoir pour tout savoir).


la source
Vous êtes le bienvenu à votre avis, mais cette réponse ne tente pas de répondre à la question du PO.
Caleb
0

Pensez à un navigateur qui affiche une page Web statique. Le modèle est le HTML. La vue est le résultat réel à l'écran.

Ajoutez maintenant du JavaScript. C'est le contrôleur. Lorsque l'utilisateur clique sur un bouton ou fait glisser quelque chose, l'événement est envoyé à JavaScript, il décide quoi faire et modifie le code HTML sous-jacent (Modèle) et le navigateur / rendu affiche ces modifications à l'écran (Affichage).

Peut-être qu'un autre bouton est cliqué, l'événement est envoyé à un gestionnaire (contrôleur) et une requête de données supplémentaires peut être envoyée à un service Web. Le résultat est ensuite ajouté au HTML (Modèle).

Le contrôleur répond aux événements et contrôle ce qui est dans le modèle et donc ce qui est à l'écran / Voir.

En revenant un peu en arrière, vous pouvez considérer le navigateur dans son ensemble comme la vue, le serveur comme le contrôleur et les données comme le modèle. Lorsque l'utilisateur clique sur un bouton dans le navigateur de l'événement qu'il a envoyé au serveur (contrôleur), il regroupe les ressources sous forme de page HTML (modèle) et les renvoie au navigateur pour les afficher (affichage).

En bas du serveur, que ce soit asp, php ou java, le 'code' (contrôleur) reçoit l'événement click, interroge une base de données ou un référentiel de documents (modèle) et crée le code HTML. Du point de vue du serveur, le résultat de toutes ses actions est une vue (HTML) de son magasin de données sous-jacent (modèle). Mais du point de vue du client, le résultat de sa demande au serveur est son modèle (HTML)

Même si vous mélangez votre code JavaScript dans votre code HTML ou votre code PHP dans votre code HTML, le modèle, vue, contrôleur existe toujours. Même si vous considérez votre demande adressée à un serveur et la réponse du serveur comme une simple rue à double sens, il existe toujours un modèle, une vue et un contrôleur.

Zeus
la source
-2

D'après mon expérience, dans un programme de bureau traditionnel, le contrôleur se retrouve spaghetti dans la vue. La plupart des gens ne prennent pas le temps de factoriser une classe de contrôleurs.

le livre de la bande de quatre dit:

Modèles de conception dans Smalltalk MVC

Le triade de classes Modèle / Vue / Contrôleur (MVC) [KP88] est utilisée pour créer des interfaces utilisateur dans Smalltalk-80. L'examen des modèles de conception dans MVC devrait vous aider à comprendre ce que nous entendons par le terme "modèle".

MVC se compose de trois types d'objets. Le modèle est l'objet d'application, la vue est sa présentation à l'écran et le contrôleur définit la manière dont l'interface utilisateur réagit aux entrées de l'utilisateur. Avant MVC, les conceptions d'interface utilisateur avaient tendance à regrouper ces objets. MVC les dissocie pour augmenter la flexibilité et la réutilisation.

MVC dissocie les vues et les modèles en établissant un protocole de souscription / notification entre eux. Une vue doit garantir que son apparence reflète l'état du modèle. Chaque fois que les données du modèle changent, le modèle notifie les vues qui en dépendent. En réponse, chaque vue obtient l’occasion de se mettre à jour. Cette approche vous permet d'attacher plusieurs vues à un modèle pour fournir différentes présentations. Vous pouvez également créer de nouvelles vues pour un modèle sans le réécrire.

Le diagramme suivant montre un modèle et trois vues. (Nous avons omis les contrôleurs pour des raisons de simplicité.) Le modèle contient des valeurs de données. Les vues définissant un tableur, un histogramme et un graphique à secteurs affichent ces données de différentes manières. Le modèle communique avec ses vues lorsque ses valeurs changent et les vues communiquent avec le modèle pour accéder à ces valeurs.

Pris au pied de la lettre, cet exemple illustre une conception qui dissocie les vues des modèles. Mais la conception s'applique à un problème plus général: découpler des objets de sorte que les modifications apportées à l'un puissent affecter un nombre quelconque d'autres objets sans qu'il soit nécessaire de connaître les détails des autres objets. Cette conception plus générale est décrite par le modèle de conception Observer (page 293).

Une autre caractéristique de MVC est que les vues peuvent être imbriquées. Par exemple, un panneau de commande de boutons peut être implémenté en tant que vue complexe contenant des vues de boutons imbriquées. L'interface utilisateur d'un inspecteur d'objet peut consister en des vues imbriquées pouvant être réutilisées dans un débogueur. MVC prend en charge les vues imbriquées avec la classe CompositeView, une sous-classe de View. Les objets CompositeView agissent exactement comme des objets View. une vue composite peut être utilisée partout où une vue peut être utilisée, mais elle contient et gère également des vues imbriquées.

Encore une fois, nous pourrions considérer cela comme une conception qui nous permet de traiter une vue composite de la même manière que nous traitons l'un de ses composants. Mais la conception est applicable à un problème plus général, qui se produit chaque fois que nous voulons grouper des objets et les traiter comme un objet individuel. Cette conception plus générale est décrite par le modèle de conception Composite (163). Il vous permet de créer une hiérarchie de classes dans laquelle certaines sous-classes définissent des objets primitifs (par exemple, Button) et d'autres classes définissent des objets composites (CompositeView) qui assemblent les primitives en objets plus complexes.

MVC vous permet également de modifier la façon dont une vue répond aux entrées de l'utilisateur sans modifier sa présentation visuelle. Vous voudrez peut-être modifier la façon dont il répond au clavier, par exemple, ou lui demander d'utiliser un menu contextuel au lieu de touches de commande. MVC encapsule le mécanisme de réponse dans un objet Controller. Il existe une hiérarchie de classes de contrôleurs, ce qui facilite la création d'un nouveau contrôleur en tant que variante d'un contrôleur existant.

Une vue utilise une instance d'une sous-classe de contrôleur pour mettre en œuvre une stratégie de réponse particulière; pour mettre en œuvre une stratégie différente, remplacez simplement l'instance par un autre type de contrôleur. Il est même possible de changer le contrôleur d'une vue au moment de l'exécution pour laisser la vue changer la façon dont elle répond aux entrées de l'utilisateur. Par exemple, une vue peut être désactivée pour ne pas accepter les entrées simplement en lui attribuant un contrôleur qui ignore les événements en entrée.

La relation Vue-Contrôleur est un exemple du modèle de conception Stratégie (315). Une stratégie est un objet qui représente un algorithme. C'est utile lorsque vous souhaitez remplacer l'algorithme de manière statique ou dynamique, lorsque vous avez beaucoup de variantes de l'algorithme ou lorsque l'algorithme a des structures de données complexes que vous souhaitez encapsuler.

MVC utilise d'autres modèles de conception, tels que Méthode d'usine (107) pour spécifier la classe de contrôleur par défaut d'une vue et Decorator (175) pour ajouter le défilement à une vue. Mais les principales relations dans MVC sont données par les modèles de conception Observer, Composite et Strategy.

Ray Tayek
la source
1
Il semble que tout ce billet, moins les deux premiers paragraphes, soit repris textuellement de Design Patterns . J'ai formaté cette section sous forme de citation afin que les lecteurs le comprennent bien - veuillez éditer si j'ai cité des paragraphes que vous avez choisis.
Caleb
1
Je suis en désaccord avec votre opinion selon laquelle "le contrôleur finit par être spaghettisé dans la vue". Cela varie peut-être avec la plate-forme et le framework que vous utilisez, mais il est beaucoup plus courant dans la programmation de Cocoa et de Cocoa Touch de créer des contrôleurs appropriés que de les omettre. Si un programmeur Objective-C omet une des catégories, c'est presque certainement le modèle qui en souffre.
Caleb
Si vous convenez qu'il s'agit de la "bonne" interprétation de MVC, MVC n'achète absolument rien. Il se peut également qu’il s’agisse simplement de MV et que vous omettiez le C, car chaque fois que vous créez une nouvelle vue, vous devez également créer un nouveau contrôleur. Alors, à quoi sert-il de s’efforcer de les séparer autrement que pour des raisons théoriques de séparation des préoccupations?
Dunk