43 votes

Comprendre le modèle MVC

J'ai du mal à comprendre le modèle MVC. Je comprends que nous essayons de découpler l'interface graphique de la logique métier, mais j'ai du mal à comprendre comment.

D'après ce que j'ai compris, le View est ce que l'utilisateur voit. Il s'agit donc généralement de la fenêtre/du formulaire. Le site Controller est entre les View et le Model . Le contrôleur fera en sorte que les données "circulent" dans les deux sens. Il va aussi faire persister l'état quand c'est nécessaire (si j'ai un assistant avec 5 étapes, c'est le Controller C'est à lui qu'il incombe de veiller à ce qu'ils soient fabriqués dans le bon ordre, etc.) Le site Model, est l'endroit où se trouve le cœur de la logique de mon application.

Ce point de vue est-il correct ?

Pour essayer de transformer cela en quelque chose de plus significatif, je vais essayer d'esquisser un exemple simple avec WinForms(pas d'ASP.NET ou de WPF, s'il vous plaît ! - pour les amateurs de Java, d'après ce que j'ai compris, Swing fonctionne de manière similaire à WinForms !), pour voir si j'ai bien compris, et je soulèverai les questions que je me pose toujours en le faisant.


Supposons que j'ai un modèle qui ne contient qu'une classe (juste pour faciliter les choses). Je sais que l'exemple aura l'air stupide mais c'est plus simple comme ça) :

class MyNumbers {
    private IList<int> listOfNumbers = new List<int> { 1, 3, 5, 7, 9 };

    public IList<int> GetNumbers() {
        return new ReadOnlyCollection<int>(listOfNumbers);
    }
}

Maintenant il est temps de faire mon Controller :

class Controller
{
    private MyNumbers myNumbers = new MyNumbers();

    public IList<int> GetNumbers() {
        return myNumbers.GetNumbers();
    }
}

El View devrait juste avoir un ListBox qui a comme éléments tous les nombres récupérés en MyNumbers .

Maintenant, la première question se pose :

Est-ce que le Controller être responsable de la création MyNumbers ? Dans ce cas simple, je pense que c'est acceptable (comme l'a fait la Commission européenne). MyNumbers fera exactement la même chose, quoi qu'il arrive, et n'a pas d'état associé). Mais supposons que je veuille utiliser pour tous les différents contrôleurs de mon application la même instance de MyNumbers . Je devrais passer à ceci Controller (et tous les autres qui en ont besoin) cette instance de MyNumbers que je veux utiliser. Qui va être responsable de cela ? Dans cet exemple WinForms, est-ce que ce serait le View ? Ou serait-ce la classe qui crée le View ?

En retournant la question : quel est l'ordre d'instanciation de ces 3 parties ? Quel est le code que le "propriétaire" de la partie MVC appelé à le créer ? Est-ce que le Controller créer à la fois le View y Model ? Est-ce que le View instancier le Controller et le Controller le site Model ?

Deuxième question :

Comment le main est censée ressembler, en supposant que je veuille que mon application ait uniquement la méthode Use Case ce Controller dépeint ?

Troisièmement :

Pourquoi, dans le diagramme MVC suivant, l'élément View ont une flèche vers le Model ? Ne devrait-on pas Controller être toujours le pont entre les deux View y Model ?

alt text


J'aurai une ou deux autres questions, mais elles auront probablement plus de sens une fois que j'aurai compris ce premier détail. Ou peut-être qu'après avoir compris cette première question, toutes les autres s'effondreront.

Gracias.

2 votes

Bonne question - J'essaie toujours de comprendre la "bonne façon" de faire du MVC.

6 votes

En quoi cette question est-elle "subjective et argumentée" ? :boggle :

0 votes

Voici un exemple basé sur le schéma cité : stackoverflow.com/questions/3072979

26voto

xj9 Points 1366

Le moyen le plus simple de se familiariser avec le MVC est de l'utiliser dans un cadre qui le met en œuvre, ceci étant dit

  • Le modèle interagit avec la source de données (BD ou autre) et vous donne accès à vos données.
  • La vue interagit avec le monde extérieur, elle reçoit des entrées de quelque part et transmet les données au contrôleur ; elle écoute également le contrôleur pour s'assurer qu'il affiche les bonnes données.
  • Le contrôleur est l'endroit où toute la magie opère ; il manipule les données, envoie des événements et gère les changements dans les deux sens (vers/depuis la vue et vers/depuis le modèle).

Ce diagramme est très utile (il est beaucoup plus logique que celui de Wikipedia) : MVC Diagram

Source : et un excellent article sur MVC !

2 votes

Excellente réponse (+1). Pouvez-vous fournir une source pour le diagramme ?

0 votes

@incrediman : à partir de l'URL de l'image : java.sun.com/developer/technicalArticles/javase/mvc En fait, je trouve la 1ère image de cet article plus claire car elle explique aussi les responsabilités de chacun.

0 votes

Donc, quand vous dites que "La vue interagit avec le monde extérieur, elle reçoit des entrées de quelque part et transmet les données au contrôleur", vous voulez dire que le contrôleur aura une référence à la ListBox et "entend" ses événements, ajoute ses éléments, etc ? Ou bien il envoie des données à la vue et c'est la vue elle-même qui ajoute les éléments, etc. Cette question me turlupine depuis un certain temps :(

3voto

RobertPitt Points 28140

Pour ce qui est des critiques formulées dans mon billet, j'ai pensé que je pourrais expliquer comment je crée un modèle MVC en PHP.

En PHP, j'ai divisé le cadre en plusieurs sections, dont certaines sont normales lorsqu'il s'agit de MVC.

Les primaires :

  • Contrôleur
  • Modèle
  • View

Détachement - Couche modèle

  • ViewLoader
  • Bibliothèque
  • Couche d'erreur

Dans le contrôleur, j'autorise généralement tous les accès aux couches secondaires et à la vue et au modèle à partir du primaire.

Voici comment je le structurerais

|---------|       |------------|       |------------|
| Browser | ----> | Controller | ----> |   Model    |
|---------|       |------------|       |------------|
     |                  |   |                |
     |                  |   |----------------|
     |                  |
     |            |------------|
     -------------|    View    |
                  |------------|

D'après mon schéma, je contourne généralement le View <-> Model et faire une Controller <-> Model et ensuite le lien de Controller <-> View affecte les données.

Dans mon cadre de travail, j'ai tendance à créer un système de stockage d'objets afin de pouvoir facilement récupérer des objets, etc.

class Registry
{
   static $storage = array();

   public static function get($key)
   {
       return isset(self::storage[$key]) ? self::storage[$key] : null;
   }

   public static function set($key,$object)
   {
       self::"storage[$key] = $object;
   }
}

Un peu plus avancé, c'est le schéma, donc avec cela, lorsque j'initialise des objets pour la première fois, je les stocke de la manière suivante Registry::set("View",new View()); afin qu'il soit toujours accessible.

Donc, dans mon contrôleur qui est le contrôleur de base, je crée plusieurs méthodes magiques. __get() __set() afin que toute classe qui étend le contrôleur puisse facilement retourner la requête par exemple :

abstract class Controller
{
   public function __get($key)
   {
       //check to make sure key is ok for item such as View,Library etc

       return Registry::get($key); //Object / Null
   }
}

Et le contrôleur de l'utilisateur

class Controller_index extends Controller
{
    public function index()
    {
       $this->View->assign("key","value"); // Exucutes a method in the View class
    }
}

Le modèle sera également placé dans le registre mais ne pourra être appelé qu'à partir de ModelLayer.

class Model_index extends ModelLayer_MySql
{
}

ou

class Model_index extends ModelLayer_MySqli
{
}

ou le système de fichiers

class Model_file extends ModelLayer_FileSystem
{
}

afin que chaque classe puisse être spécifique au type de stockage.

Ce n'est pas le type traditionnel de modèle MVC, mais il peut être appelé MVC adoptif.

D'autres objets tels que le View Loader ne doivent pas être placés dans le registre car ils ne servent pas spécifiquement les intérêts des utilisateurs mais sont utilisés par d'autres entités telles que View.

abstract class ViewLoader
{
   function __construct($file,$data) //send the file and data
   {
       //Include the file and set the data to a local variable
   }

   public function MakeUri()
   {
       return Registry::get('URITools')->CreateURIByArgs(func_get_args());
   }
}

comme le fichier modèle est inclus dans le chargeur de vue et NON dans la classe de vue, cela sépare les méthodes de l'utilisateur des méthodes du système et permet également aux méthodes d'être utilisées dans les vues elles-mêmes pour la logique générale.

Exemple de fichier modèle.

<html>
   <body>
      <?php $this->_include("another_tpl_file.php"); ?>
      <?php if(isset($this->session->admin)):?>

          <a href="<?php echo $this->MakeUri("user","admin","panel","id",$this->session->admin_uid) ?>"><?php echo $this->lang->admin->admin_link ?></a>

      <?php endif; ?>
   </body>
</html>

J'espère que mes exemples vous aideront à comprendre un peu mieux.

2voto

Zoltan Balazs Points 699

Réponse à la troisième question :

Lorsque le modèle change, il le notifie à la vue, puis la vue obtient les données du modèle en utilisant ses getters.

0 votes

Est-ce une question ou une déclaration ?

2 votes

Mais ne sommes-nous pas en train d'ajouter un couplage inutile entre la vue et le modèle ? Ne devrions-nous pas simplement utiliser le contrôleur comme une sorte de proxy ? Qui a la responsabilité de transformer les données IList<int> en éléments dans la ListBox ? La vue ou le contrôleur ? Le contrôleur doit-il montrer l'IList à la vue ou doit-il, par exemple, avoir une référence à la ListBox et ajouter des éléments à la ListBox elle-même ? Merci

0 votes

Voyons un exemple : Vous avez des données importantes et vous voulez les montrer à vos utilisateurs. Vous avez donc un modèle, qui peut être une liste. La vue obtient la liste à partir du modèle, et la vue sait comment montrer les données. La vue peut les montrer dans un tableau, un diagramme, etc...

1voto

hvgotcodes Points 55375

"D'après ce que j'ai compris, la vue, c'est ce que l'utilisateur voit. Il s'agit donc généralement de la fenêtre/du formulaire. Le contrôleur se situe entre la vue et le modèle. Le contrôleur va "gérer" les données dans les deux sens. Il fait également persister l'état si nécessaire (si j'ai un assistant avec 5 étapes, c'est la responsabilité du contrôleur de s'assurer qu'elles sont faites dans le bon ordre, etc.) Le modèle est l'endroit où se trouve le cœur de la logique de mon application."

C'est presque correct. Le contrôleur ne fait pas persister les données. Il appelle un service qui persiste les données. La raison en est que la persistance des données n'est jamais un simple appel à la sauvegarde. Il se peut que vous souhaitiez effectuer des contrôles de validation sur les données pour vous assurer qu'elles sont saines et conformes aux besoins de votre entreprise. Il se peut que vous souhaitiez procéder à une authentification pour vous assurer que les données peuvent être sauvegardées par un utilisateur. Si vous faites cela dans un service, vous disposez alors d'un bel ensemble de fonctionnalités que vous pouvez utiliser à l'infini, par exemple pour une application Web et un service Web. Si vous le faites dans un contrôleur, par exemple pour une application Web, lorsque vous écrirez votre service Web, vous devrez remanier et/ou dupliquer le code.

En réponse à votre commentaire "Je ne suis pas sûr d'avoir totalement compris votre point de vue. Est-ce que le contrôleur vérifie l'entrée de l'interface utilisateur, ou est-ce le modèle qui le fait ?"

Votre contrôleur ne doit contrôler que les chemins de la fonctionnalité métier qui sont exécutés. C'est ça. Les contrôleurs doivent être la partie du code la plus facile à écrire. Vous pouvez effectuer une certaine validation sur l'interface (c'est-à-dire la vue, par exemple en vous assurant que les adresses électroniques sont correctement formatées, que les entrées de texte ne dépassent pas les maximums), mais la couche métier devrait également valider l'entrée - pour la raison que j'ai mentionnée précédemment, à savoir que lorsque vous commencez à mettre en place plus de points d'extrémité, vous n'avez pas besoin de procéder à un remaniement.

0 votes

J'ai utilisé le mauvais terme en disant "gérer". Je voulais dire qu'il ne fallait pas "travailler" avec, mais simplement le faire circuler, de la vue au contrôleur, et du contrôleur à la vue.

1 votes

Bien sûr. Je pense que tu as compris. Mes commentaires portaient davantage sur la façon d'utiliser MVC avec certains choix de conception communs qui vous permettent d'écrire des composants réutilisables. Vous savez que vous vous y prenez mal si votre modèle gère les préoccupations de la vue (comme la sortie html), ou si votre contrôleur gère la logique métier (comme décider comment formater les valeurs à sauvegarder), etc.....

1voto

Awaken Points 515

Ceci est tiré de Java, mais j'espère que cela vous aidera.

Pour le principal :

public static void main(String[] args) 
{
       MyNumbers myNums = new MyNumbers();  // Create your Model
       // Create controller, send in Model reference.      
       Controller controller = new Controller(myNums); 
}

Votre contrôleur a besoin d'une référence à votre modèle. Dans ce cas, le contrôleur crée réellement tous les composants Swing. En C#, vous pouvez laisser l'initialisation du formulaire ici, mais la vue/formulaire a besoin d'une référence au modèle (myNums) et au contrôleur (controller). J'espère que certains utilisateurs de C# pourront vous aider sur ce point. La vue doit également s'enregistrer en tant qu'observateur du modèle (voir Observer Pattern).

Voici le constructeur que j'ai (ajusté pour votre cas) :

public NumberView(Controller controller, MyNumbers myNums)
{
      this.controller = controller; // You'll need a local variable for this
      this.myNums = myNums; //You'll need a local variable for this
      myNums.registerObserver(this); // This is where it registers itself
}

La vue transmet le travail au contrôleur pour qu'il gère les actions de l'utilisateur (boutons, etc.). Le contrôleur décide de ce qu'il faut appeler/faire dans le modèle. En général, le modèle fait alors quelque chose et change d'état (peut-être plus de chiffres dans votre liste, peu importe ce qu'il fait). À ce moment-là, le modèle fait savoir à ses observateurs qu'il a changé et qu'ils doivent se mettre à jour. Ensuite, la vue va chercher les nouvelles données et se met à jour. C'est pourquoi le modèle et la vue se parlent (votre troisième question).

Donc le modèle aura :

public void notifyObservers()
{
    for (Observer o: observers)
    {
        o.update();  // this will call the View update below since it is an Observer
    }
}

Ainsi, dans la vue, vous aurez quelque chose comme ceci :

public void update()
{
    setListBox(myNums.getNumbers());  // Or whatever form update you want
}

J'espère que cela vous aidera. Je sais que c'est du Java, mais le concept s'applique toujours. Vous devrez lire un peu sur le modèle Observer pour le comprendre pleinement. Bonne chance !

0 votes

Alors, est-ce que le contrôleur instancie la vue ?

1 votes

Désolé, j'ai appuyé sur entrée prématurément. J'espère que la réponse complète vous aidera. Faites-moi savoir si vous avez besoin d'explications supplémentaires sur un point quelconque.

0 votes

Tout d'abord, si je comprends bien, vous faites référence à "MVC 1", tel qu'il apparaît sur le diagramme de mon message original, par opposition à celui d'indieinvader, n'est-ce pas ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X