Le principe de responsabilité unique stipule qu '"une classe devrait avoir une raison de changer".
Dans le modèle MVC, le travail du contrôleur consiste à assurer la médiation entre la vue et le modèle. Il offre une interface pour que la vue signale les actions effectuées par l'utilisateur sur l'interface graphique (par exemple, permettant à la vue d'appeler controller.specificButtonPressed()
), et est capable d'appeler les méthodes appropriées sur le modèle afin de manipuler ses données ou d'appeler ses opérations (par exemple model.doSomething()
) .
Cela signifie que:
- Le contrôleur doit connaître l'interface graphique, afin d'offrir à la vue une interface appropriée pour signaler les actions de l'utilisateur.
- Il doit également connaître la logique du modèle, afin de pouvoir invoquer les méthodes appropriées sur le modèle.
Cela signifie que cela a deux raisons de changer : un changement dans l'interface graphique et un changement dans la logique commerciale.
Si l'interface graphique change, par exemple un nouveau bouton est ajouté, le contrôleur peut avoir besoin d'ajouter une nouvelle méthode pour permettre à la vue de signaler qu'un utilisateur appuie sur ce bouton.
Et si la logique métier du modèle change, le contrôleur devra peut-être changer pour appeler les bonnes méthodes sur le modèle.
Par conséquent, le contrôleur a deux raisons possibles de changer . Est-ce que cela brise le SRP?
la source
Réponses:
Si vous continuez par conséquent à raisonner sur le PÉR, vous remarquerez que la «responsabilité unique» est en fait un terme spongieux. Notre cerveau humain est en quelque sorte capable de faire la distinction entre différentes responsabilités et plusieurs responsabilités peuvent être résumées en une seule responsabilité «générale». Par exemple, imaginez que dans une famille de 4 personnes, il y ait un membre de la famille responsable de préparer le petit-déjeuner. Maintenant, pour ce faire, il faut faire bouillir des œufs et du pain grillé et bien sûr mettre en place une bonne tasse de thé vert (oui, le thé vert est le meilleur). De cette façon, vous pouvez diviser "faire le petit déjeuner" en petits morceaux qui sont ensemble abstraits de "faire le petit déjeuner". Notez que chaque pièce est également une responsabilité qui pourrait par exemple être déléguée à une autre personne.
Revenons au MVC: si la médiation entre le modèle et la vue n'est pas une responsabilité mais deux, alors quelle serait la prochaine couche d'abstraction ci-dessus, combinant ces deux? Si vous ne pouvez pas en trouver un, vous ne l'avez pas résumé correctement ou il n'y en a pas, ce qui signifie que vous avez tout compris. Et je pense que c'est le cas avec un contrôleur, manipulant une vue et un modèle.
la source
Si une classe a "deux raisons possibles de changer", alors oui, elle viole SRP.
Un contrôleur doit généralement être léger et avoir la seule responsabilité de manipuler le domaine / modèle en réponse à un événement piloté par l'interface graphique. Nous pouvons considérer chacune de ces manipulations comme des cas d'utilisation ou des fonctionnalités.
Si un nouveau bouton est ajouté sur l'interface graphique, le contrôleur ne devrait avoir à changer que si ce nouveau bouton représente une nouvelle fonctionnalité (c'est-à-dire contrairement au même bouton qui existait à l'écran 1 mais n'existait pas encore à l'écran 2, et c'est alors ajouté à l'écran 2). Il faudrait également un nouveau changement correspondant dans le modèle pour prendre en charge cette nouvelle fonctionnalité / caractéristique. Le contrôleur a toujours la responsabilité de manipuler le domaine / modèle en réponse à un événement piloté par l'interface graphique.
Si la logique métier du modèle change en raison d'un bug corrigé et que le contrôleur doit changer, alors c'est un cas spécial (ou peut-être que le modèle viole le principal ouvert-fermé). Si la logique métier du modèle change pour prendre en charge de nouvelles fonctionnalités / fonctionnalités, cela n'impacte pas nécessairement le contrôleur - uniquement si le contrôleur a besoin d'exposer cette fonctionnalité (ce qui serait presque toujours le cas, sinon pourquoi serait-il ajouté à le modèle de domaine s'il ne sera pas utilisé). Donc, dans ce cas, le contrôleur doit également être modifié pour prendre en charge la manipulation du modèle de domaine de cette nouvelle manière en réponse à un événement piloté par l'interface graphique.
Si le contrôleur doit changer parce que, disons, la couche de persistance est passée d'un fichier plat à une base de données, alors le contrôleur viole certainement SRP. Si le contrôleur fonctionne toujours sur la même couche d'abstraction, cela peut aider à atteindre SRP.
la source
Le contrôleur ne viole pas SRP. Comme vous le dites, sa responsabilité est de faire la médiation entre les modèles et la vue.
Cela étant dit, le problème avec votre exemple est que vous liez les méthodes du contrôleur à la logique dans la vue, c'est-à-dire
controller.specificButtonPressed
. Nommer les méthodes de cette manière lie le contrôleur à votre interface graphique, vous n'avez pas réussi à abstraire correctement les choses. Le contrôleur doit être sur l'exécution d'actions spécifiques, c'estcontroller.saveData
-à- dire oucontroller.retrieveEntry
. L'ajout d'un nouveau bouton dans l'interface graphique ne signifie pas nécessairement l'ajout d'une nouvelle méthode au contrôleur.En appuyant sur un bouton dans la vue, cela signifie faire quelque chose, mais quoi que ce soit aurait pu facilement être déclenché de plusieurs façons ou même pas à travers la vue.
Extrait de l'article Wikipedia sur SRP
Le contrôleur ne se préoccupe pas uniquement de ce qui se trouve dans la vue, mais lorsque l'une de ses méthodes est appelée, il fournit des données spécifiées à la vue. Il n'a besoin de connaître les fonctionnalités du modèle que dans la mesure où il sait qu'il doit appeler les méthodes dont il dispose. Il ne sait rien de plus que cela.
Savoir qu'un objet a une méthode disponible pour appeler n'est pas la même chose que connaître sa fonctionnalité.
la source
specificButtonsPressed()
c'est parce que j'ai lu que la vue ne devrait rien savoir sur la fonctionnalité de ses boutons et autres éléments de l'interface graphique. On m'a appris que lorsqu'un bouton est enfoncé, la vue doit simplement faire rapport au contrôleur, et le contrôleur doit décider «ce que cela signifie» (puis invoquer les méthodes appropriées sur le modèle). Faire l'appel de vuecontroller.saveData()
signifie que la vue doit savoir ce que signifie cette pression sur un bouton, en plus du fait qu'elle a été pressée.specificButtonPressed()
), en effet, le contrôleur ne serait pas tellement lié à l'interface graphique. Dois-je abandonner lesspecificButtonPressed()
méthodes? Est-ce que l'avantage que je pense avoir ces méthodes a du sens pour vous? Ou est-ce que le fait d'avoir desbuttonPressed()
méthodes dans le contrôleur ne vaut pas la peine?specificButtonPressed()
méthodes dans le contrôleur, c'est qu'il sépare complètement la vue de la signification de ses boutons . Cependant, l'inconvénient est qu'il lie le contrôleur à l'interface graphique dans un sens. Quelle approche est la meilleure?foo
, ou tout aussi facilementfireZeMissiles
. Il saura seulement qu'il doit se rapporter à une fonction spécifique. Il ne sait pas ce que fait la fonction qui l'appellera. Le contrôleur ne se soucie pas de la façon dont ses méthodes sont invoquées, juste qu'il répondra d'une certaine manière quand elles le seront.La responsabilité unique d'un contrôleur est d'être le contrat qui sert de médiateur entre la vue et le modèle. La vue ne doit être responsable que de l'affichage, le modèle ne doit être responsable que de la logique métier. C'est la responsabilité des contrôleurs de relier ces deux responsabilités.
C'est bien beau, mais s'aventurer un peu loin du milieu universitaire; un contrôleur dans MVC est généralement composé de nombreuses méthodes d'action plus petites. Ces actions correspondent généralement à des choses qu'une chose peut faire. Si je vends des produits, j'aurai probablement un ProductController. Ce contrôleur aura des actions comme GetReviews, ShowSpecs, AddToCart ect ...
La vue a le SRP d'afficher l'interface utilisateur, et une partie de cette interface utilisateur comprend un bouton qui dit AddToCart.
Le contrôleur a le SRP de connaître toutes les vues et modèles impliqués dans le processus.
L'action AddToCart des contrôleurs a le SRP spécifique de connaître toutes les personnes qui doivent être impliquées lorsqu'un article est ajouté à un panier.
Le modèle de produit a le SRP de modélisation de la logique du produit, et le modèle de ShoppingCart a le SRP de modélisation de la façon dont les articles sont enregistrés pour une extraction ultérieure. Le modèle utilisateur a un SRP de modélisation de l'utilisateur qui ajoute des éléments à son panier.
Vous pouvez et devez réutiliser des modèles pour faire fonctionner votre entreprise et ces modèles doivent être couplés à un moment donné dans votre code. Le contrôleur contrôle chaque façon unique dont le couplage se produit.
la source
Les contrôleurs n'ont en fait qu'une seule responsabilité: modifier l'état des applications en fonction de l'entrée de l'utilisateur.
source: wikipedia
Si vous avez plutôt des "contrôleurs" de style Rails (qui jonglent avec des instances d'enregistrement actives et des modèles stupides) , alors bien sûr, vous brisez SRP.
Là encore, les applications de style Rails ne sont pas vraiment MVC pour commencer.
la source