Cours BUT Info

Ch. 3 — Modèle, Vue(s), Contrôleur(s)

Le patron architectural MVC : découpler l'interface de la logique métier — un modèle unique qui notifie, des vues qui affichent, des contrôleurs qui traduisent les actions de l'utilisateur.

Un patron… architectural

Jusqu'ici, les patrons de conception concernaient quelques classes au plus. Un patron architectural organise à grande échelle : il structure « toutes » les classes du système. Le plus célèbre est le MVC — Modèle / Vue / Contrôleur — dont l'objectif tient en une phrase : découpler l'IHM de la logique applicative.

Ce patron a nourri des travaux en IHM et en génie logiciel, et il a des variations modernes : Modèle-Vue-Présentation (MVP), architecture en 3 couches, etc.

La triade MVC

Le trio du MVC : le modèle au centre (données + règles métier), des vues qui ne font que montrer, des contrôleurs qui traduisent les actions de l'utilisateur. Les échanges entre modèle et vues suivent le patron Observable du cours 2 — variante « pull » (la vue va chercher) ou « push » (le modèle envoie).
RôleC'est quoiPoints clés
Modèlele cœur fonctionnel : données, aspects métier, règlesunique (mais composé de plusieurs classes), indépendant de la vue, contient le plus de traitement possible
Vue(s)la représentation graphique des données du modèleplusieurs possibles, pas de traitement métier, ne modifie pas le modèle directement
Contrôleur(s)associé(s) à une vue : récupère les événements utilisateurpeut vérifier la validité des données, transmet au modèle — pas de vérifications métier

Le Modèle en détail

  • Le modèle est unique — mais il peut être composé de plusieurs classes : c'est une organisation abstraite du code, pas un seul fichier.
  • Il est complètement indépendant de la vue : le même modèle doit pouvoir fonctionner sous JavaFX ou Android — donc pas d'import javafx.* dans le modèle.
  • Il doit contenir le plus de traitement possible.
  • Quand il change (valeur d'un attribut), il avise chaque vue — et chaque vue se met à jour, ou pas : c'est elle qui décide.
  • Il ne dit jamais à la vue quoi afficher ni comment l'afficher.

Les Vues en détail

  • Il peut y avoir plusieurs vues — au choix, ou simultanément.
  • Une vue n'est pas nécessairement une classe : c'est une notion logique — une fenêtre, ou une partie cohérente d'une fenêtre. Elle peut être composée de plusieurs objets.
  • Elle ne contient que du code relié à l'affichage.
  • Elle ne modifie pas le modèle directement (mais elle peut lui passer des instructions de l'utilisateur : incrémenter/décrémenter une valeur, transmettre une saisie).
  • Elle ne fait pas de calcul qui pourrait être dans le domaine.

Les Contrôleurs en détail

  • Associés aux vues — donc plusieurs contrôleurs possibles, chacun avec plusieurs classes.
  • Ils récupèrent les événements et les nouvelles données de la vue.
  • Ils peuvent faire des vérifications simples : « est-ce que cette date existe ? ».
  • Ils ne font pas de vérifications liées au métier : « est-ce que la date de facturation correspond à l'année fiscale ? » — ça, c'est le domaine du modèle.
  • Ils transmettent au modèle, qui se met à jour en conséquence.

Le défi : notifier des vues inconnues

Comment le modèle fait-il pour notifier ses vues, alors que leur nombre est inconnu (aucune, une, plusieurs) et leur type aussi (JavaFX, Android, web…) ? C'est exactement le problème résolu par le patron Observable / Observateurs du cours 2 : le modèle est observable, les vues s'abonnent à lui et se mettent à jour à chaque notification. Le MVC s'appuie donc sur le patron Observer — les deux chapitres se répondent.

Deux interprétations du cycle — nous utiliserons la première

Dans l'interprétation retenue par le cours : la vue passe l'action au contrôleur, qui modifie le modèle ; le modèle notifie les vues ; les vues récupèrent les données auprès du modèle (mode pull, cours 2). D'autres projets font interagir la vue et le contrôleur de façon plus symétrique (« voit », « interagit ») — les deux variantes circulent, il faut savoir les reconnaître.

Exemple du cours : une calculatrice très simple

L'application : un champ de saisie (3 + 2), un bouton « Calcul ! », un champ résultat (5).

L'approche « intuitive » (sans MVC)

bouton.setOnAction((ev) -> {
    String entree = txtEntree.getText();
    int resultat = calculResultat(entree);
    txtResultat.setText("" + resultat);
});

Tout tient dans le gestionnaire d'événement… et c'est là le problème : plus difficile à tester (le calcul est prisonnier de la fenêtre), et si on ajoute une deuxième vue, on duplique la logique.

L'approche MVC

  • Vue : fenêtre, bouton, champs texte ;
  • Contrôleur : l'ActionListener du bouton ;
  • Modèle : l'interprétation de la formule saisie et le calcul du résultat.
La calculatrice du cours : la vue ne calcule rien, le contrôleur ne fait que transmettre la saisie, le modèle interprète l'expression et notifie. Le calcul vit dans le modèle — testable sans ouvrir de fenêtre.

Le contrôleur, ici intégré dans la vue, ne fait que transmettre :

// Dans la vue : l'action du bouton délègue au modèle
bouton.setOnAction((ev) -> {
    String entree = txtEntree.getText();
    this.modele.majResultat(entree);
});

Le modèle fait le travail et notifie ses observateurs :

public class CalculetteModele {
    protected int resultat;
    protected Set<CalculetteObservateur> obs;

    public void majResultat(String entree) {
        this.resultat = this.calculResultat(entree);
        this.notifyObservers();
    }
    // + méthodes add / remove / notifyObservers (cours 2)
}

La vue, observatrice du modèle, récupère les données à la notification (mode pull) :

class CalculetteVue implements CalculetteObservateur {

    public CalculetteVue(CalculetteModele modele) {
        modele.add(this); // abonnement
        // ...
    }

    public void update(CalculetteModele modele) {
        int resultat = modele.getResultat(); // la vue va chercher la donnée
        txtResultat.setText("" + resultat);
    }
}

Variante mode push : le modèle envoie directement la donnée dans la notification (notifyObservers(resultat)), et la vue la reçoit (update(int resultat)).

Contrôleur séparé : la version complète

On peut aussi sortir le contrôleur dans sa propre classe — c'est la version la plus propre :

public class CalculetteControleur {
    protected CalculetteModele modele;
    protected CalculetteVue vue;

    public void appuiSurEntree() {
        this.modele.majResultat(vue.getEntree());
    }
}

La vue crée le contrôleur et lui délègue l'action du bouton (this.controleur.appuiSurEntree()). Chacun son rôle : la vue montre, le contrôleur traduit, le modèle calcule.

A retenir

Patron architectural :

  • organisation logique de classes à grande échelle ;
  • favorise l'indépendance des parties (surtout le modèle) ;
  • plusieurs variations existent (MVP, 3 couches…).

Patron MVC :

  • modèle unique, indépendant du reste, qui contient le métier ;
  • plusieurs vues, pas de traitements métier, qui observent le modèle ;
  • plusieurs contrôleurs, qui gèrent les actions utilisateur et informent le modèle.

ch3-mvc.pdf

R3.04 · Cours 3 — Fabien Delecroix, base de Nicolas Anquetil · 28 diapositives

Consultation seule — téléchargement désactivé

Chargement du document…

Dernière mise à jour le

Sur cette page