A31 - Conception et programmation objet avancées

3 - Modèle-Vue-Contrôleur

Adrien Krähenbühl IUT Robert Schuman

Modèle-Vue-Contrôleur

Le design pattern Modèle-Vue-Contrôleur (MVC) est un design pattern qui propose une architecture logicielle, c’est-à-dire une organisation de toutes les classes d’une application.

Problème
Comment rendre les données d’une application indépendantes des interface utilisateur qui les affichent ?

Rôles des 3 modules du MVC

Ensemble des classes qui représentent et manipulent les données. On peut parler de classes “métier”.

Ensemble des classes qui gèrent la présentation des données à l’utilisateur. La vue peut aussi être le point d’entrée des requêtes de l’utilisateur (saisie de texte, choix d’une action, etc.).

Ensemble des classes qui coordonnent le modèle et la vue pour répondre aux demandes de l’utilisateur.

Échanges entre les modules

 

Qui peut faire quoi ?

Modèle
  • Ne connait ni le contrôleur ni la vue.
  • Contient toute la logique de l’application
Vue
  • Possède les éléments sur lesquels l’utilisateur peut agir
  • Délègue la gestion des évènement utilisateurs au contrôleur
  • NE MODIFIE JAMAIS LE MODÈLE
Contrôleur
  • Modifie le modèle en fonction des demandes de l’utilisateur
  • Peut changer de vue
  • Peut modifier le modèle
  • Coordonne l’enchainement de la modification des données et de la mise à jour de la vue

Exemple avec un livre

public static void main( String[] args ) {
    Book b1 = new Book("Default title");
    System.out.println("Title : "+b1.getTitle());
    Scanner scan = new Scanner(System.in);
    System.out.println("Enter a book title :");
    String title = scan.nextLine();  // Read user input
    b1.setTitle(title);
    System.out.println("New title : "+b1.getTitle());
}

public static void main( String[] args ) {
  Book b1 = new Book("Default title");
  Controller control = new Controller(b1);
  View view = new View(b1,control);

  view.showBook();
  view.editBookTitle();
  System.out.println("New title : "+b1.getTitle());
}
class Controller {
  public Controller( Book book ) {
    this.book = book;
  }
  public void modifyBookTitle( String title ) {
    book.setTitle(title);
  }
}
class View {
  public View( Book book, Controller controller ) {
    this.book = book;
    this.controller = controller;
  }
  public void editBookTitle() {
    Scanner scan = new Scanner(System.in);
    System.out.println("Enter a book title :");
    String title = scan.nextLine();
    controller.modifyBookTitle(title);
  }
  public void showBook() {
    System.out.println("Title : "+book.getTitle());
  }
}

public static void main( String[] args ) {
  Book b1 = new Book("Default title");b1.getTitle()
  Controller control = new Controller(b1);
  View view = new View(b1,control);

  book.addObserver(view);

  view.showBook();
  view.editBookTitle();
}
class Controller {
  public Controller( Book book ) {
    this.book = book;
  }
  public void modifyBookTitle( String title ) {
    book.setTitle(title);
  }
}
class View implements BookObserver {
  public View( Book book, Controller controller ) {
    this.book = book;
    this.controller = controller;
  }
  public void editBookTitle() {
    Scanner scan = new Scanner(System.in);
    System.out.println("Enter a book title :");
    String title = scan.nextLine();
    controller.modifyBookTitle(title);
  }
  public void showBook() {
    System.out.println("Title : "+book.getTitle());
  }
  public void updateBookTitle( String title ) {
    System.out.println("New title : "+title);
  }
}
class Book {
  public void setTitle( String title ) {
    this.title = title;
    notifyTitleChange(title);
  }
}

À retenir

MVC est à utiliser pour des applications conséquentes : c’est totalement inutile pour l’exemple minimaliste du livre.

MVC est intimement lié au design pattern Observer qui réalise de l’injection de dépendance.

Les autres Modèle-Vue-Truc

Model-View-Adapter (MVA)
  • Aucune communication directe entre Modèle et Vue
  • Un pattern Adapter (ou Médiator) prend en charge les communications
  • Il peut y avoir plusieurs Adaptateurs entre le Modèle et la Vue
Model-View-Presenter (MVP)
  • La Vue transmet les commandes au Présentateur
  • Le Présentateur encapsule la logique de présentation
  • Le Présentateur transmet les infos du Modèle à la Vue
Model-View-ViewModel (MVVM)
  • Mélange des deux précédents
  • Le composant ViewModel :
    • sert de médiateur pour convertir les données du Modèle
    • encapsule la logique de présentation pour la Vue

Dépendance entre classes

Une classe MaClasse dépend d’une classe Dependance :

1 - Par composition
Si MaClasse possède un attribut de type Dependance.
2 - Par héritage
Si MaClasse est de type Dependance.
3 - Par transitivité
Si MaClasse dépend d’un objet de type Intermediaire qui dépend d’un objet de type Dependance.
4 - Par utilisation
Si une méthode de MaClasse appelle une méthode de Dependance.

L’injection de dépendance

Principe :
Utiliser des interfaces pour ne pas dépendre des classes concrètes.

class Maclasse {
    private Dependance attribut;

    public MaClasse( Dependance dep ) {
        attribut = dep;
    }
}

public static void main(String[] args) {
    Dependance dep = new Dependance();
    MaClasse maClasse = new MaClasse(dep);
}


1. À l’instanciation.

class Maclasse {
    private Dependance attribut;

    public MaClasse( Dependance dep ) {
        attribut = dep;
    }
}

public static void main(String[] args) {
    Dependance dep = new Dependance();
    MaClasse maClasse = new MaClasse(dep);
}

Sans injection de dépendances.

class Maclasse {
    private InterfaceDependance attribut;

    public MaClasse( InterfaceDependance dep ) {
        attribut = dep;
    }
}

public static void main(String[] args) {
    InterfaceDependance dep = new Dependance();
    MaClasse maClasse = new MaClasse(dep);
}

Avec injection de dépendances.


2. Par modification.

class Maclasse {
    private Dependance attribut;

    public MaClasse() {}

    public setDependance( Dependance dep ) {
        attribut = dep;
    }
}

public static void main(String[] args) {
    Dependance dep = new Dependance();
    MaClasse maClasse = new MaClasse();
    maClasse.setDependance(dep);
}

Sans injection de dépendances.

class Maclasse {
    private InterfaceDependance attribut;

    public MaClasse() {}

    public setDependance( InterfaceDependance dep ) {
        attribut = dep;
    }
}

public static void main(String[] args) {
    InterfaceDependance dep = new Dependance();
    MaClasse maClasse = new MaClasse();
    maClasse.setDependance(dep);
}

Avec injection de dépendances.

Quelques liens utiles

À lire avant toute chose
C’est quoi être professionnel quand on est développeur ?
SOLID et GRASP
Patrons du GoF
Autres