Christophe Jacquet, Associate Professor at Supélec

Enseignement > Exemples en Java >

Utilisation du paradigme MVC avec la bibliothèque Swing

Il est problématique de lier l'interface homme-machine (IHM) d'un logiciel avec ses données et traitements métiers. En effet, un simple changement d'IHM ou de bibliothèque d'IHM implique une refonte totale de l'application.

C'est pourquoi le paradigme Modèle-Vue-Contrôleur (MVC) a été introduit. Il préconise une séparation entre trois entités :

En Java, le modèle correspond à vos types métiers, tandis que vue et contrôleur sont implémentés par les composants graphiques de Swing : JList, JTable, JTree, etc.

Cet exemple explique comment il est possible de concevoir un type métier (une sorte d'arbre) sans se préoccuper de la façon dont il sera utilisé dans l'interface homme-machine. Un adaptateur permet alors à un composant JTree d'y accéder au travers de l'interface standard TreeModel.

Le code est réparti en trois classes :

/* ############################################################################
 * 
 * Exemple de séparation de la logique métier d'une application et l'interface
 * Swing associée, selon le paradigme MVC (Modèle-Vue-Contrôleur).
 *
 * Noeud : représente un type métier très simple, qui permet de construire
 *         des arbres.
 * 
 * Auteur : Christophe Jacquet, Supélec
 * 
 * Historique
 * 2006-12-04  Ajout de commentaires détaillés
 * 2006-05-19  Création
 * 
 * ############################################################################
 */

package treemodel;

import java.util.Arrays;
import java.util.List;

/**
 * Classe Noeud, qui correspond a notre type métier. Très simplement, un arbre
 * est constitue de Noeuds.
 *
 * Cette classe donne diverses méthodes d'accès à un noeud.
 *
 * NB : aucune dependance à un type quelconque de la bibliotheque Swing !
 */
public class Noeud {
	private final String etiquette;
	private final List<Noeud> enfants;
	
	/**
	 * Construit un noeud étant données une étiquette et une liste d'enfants.
	 * 
	 * @param etiquette étiquette du noeud
	 * @param enfants une liste contenant les enfants
	 */
	public Noeud(String etiquette, List<Noeud> enfants) {
		this.etiquette = etiquette;
		this.enfants = enfants;
	}

	/**
	 * Construit un noeud étant données une étiquette et des enfants tous
	 * passés en paramètres.
	 * 
	 * @param etiquette étiquette du noeud
	 * @param enfants les enfants
	 */
	public Noeud(String etiquette, Noeud ... enfants) {
		this(etiquette, Arrays.asList(enfants));
	}
	
	/**
	 * Renvoie l'enfant numéro i.
	 * 
	 * @param i numéro de l'enfant
	 * @return l'objet Noeud correspondant
	 */
	public Noeud enfantNumero(int i) {
		return enfants.get(i);
	}
	
	/**
	 * Renvoie le numéro d'un enfant donné.
	 * 
	 * @param enfant l'enfant dont on veut connaître le numéro
	 * @return le numéro de l'enfant, ou -1 si ce n'est pas un enfant
	 */
	public int numeroEnfant(Noeud enfant) {
		return enfants.indexOf(enfant);
	}
	
	/**
	 * Renvoie le nombre d'enfants du noeud courant.
	 * 
	 * @return le nombre d'enfants
	 */
	public int nombreEnfants() {
		return enfants.size();
	}
	
	/**
	 * Renvoie une représentation textuelle du noeud.
	 * 
	 * @return une chaîne répresentant le noeud
	 */
	public String toString() {
		return etiquette;
	}
	
	/**
	 * Renvoie une répresentation textuelle du sous-arbre correspondant à ce
	 * noeud.
	 * 
	 * @return une chaîne representant le sous-arbre
	 */
	public String sousArbre() {
		String res = etiquette + "(";
		for(Noeud enfant : enfants) res += enfant.sousArbre() + " ";
		return res + ")";
	}
}
/* ############################################################################
 * 
 * Exemple de séparation de la logique métier d'une application et l'interface
 * Swing associée, selon le paradigme MVC (Modèle-Vue-Contrôleur).
 *
 * Adaptateur : classe d'adaptation, qui permet d'obtenir un TreeModel (i.e.
 *              un objet qui implémente l'interface TreeModel) sur un arbre
 *              constitué de nos Noeuds. Les arbres sont considérés comme
 *              constants, donc toutes les méthodes ne sont pas implémentées.
 * 
 * Auteur : Christophe Jacquet, Supélec
 * 
 * Historique
 * 2006-12-04  Ajout de commentaires détaillés
 * 2006-05-19  Création
 * 
 * ############################################################################
 */

package treemodel;

import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

/**
 * 
 */
public class Adaptateur implements TreeModel {
	private final Noeud racine;
	
	/**
	 * Construit un nouvel adaptateur pour l'arbre dont on donne la racine.
	 * 
	 * @param racine la racine de l'arbre
	 */
	public Adaptateur(Noeud racine) {
		this.racine = racine;
	}
	
	public Object getRoot() {
		return racine;
	}
	
	public Object getChild(Object parent, int index) {
		if(parent != null && parent instanceof Noeud) {
			return ((Noeud)parent).enfantNumero(index);
		} else return null;
	}

	public int getChildCount(Object parent) {
		if(parent != null && parent instanceof Noeud) {
			return ((Noeud)parent).nombreEnfants();
		}  else return 0;
	}

	public boolean isLeaf(Object node) {
		return getChildCount(node) == 0;
	}

	public void valueForPathChanged(TreePath path, Object newValue) {
		// pas implementé
	}

	public int getIndexOfChild(Object parent, Object child) {
		if(parent != null && parent instanceof Noeud && child instanceof Noeud) {
			return ((Noeud)parent).numeroEnfant((Noeud)child);
		} else return -1;
	}

	public void addTreeModelListener(TreeModelListener l) {
		// pas implémenté
	}

	public void removeTreeModelListener(TreeModelListener l) {
		// pas implémenté
	}
}
/* ############################################################################
 * 
 * Exemple de séparation de la logique métier d'une application et l'interface
 * Swing associée, selon le paradigme MVC (Modèle-Vue-Contrôleur).
 *
 * Essai : programme principal d'essai. Il crée un arbre constitué de Noeuds, 
 *         puis une interface graphique Swing qui comporte un JTree. À l'aide
 *         de l'adaptateur entre Noeud et TreeModel, ce JTree est capable
 *         de lire et de représenter les données du JTree.
 * 
 * Auteur : Christophe Jacquet, Supélec
 * 
 * Historique
 * 2006-12-04  Ajout de commentaires détaillés
 * 2006-05-19  Création
 * 
 * ############################################################################
 */

package treemodel;

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.TreeModel;


public class Essai {
	public static void main(String[] args) {
		// creation de l'arbre, uniquement à l'aide de la classe Noeud
		final Noeud arbre = new Noeud("Langue", 
				new Noeud("Asiatique", new Noeud("Mandarin"), new Noeud("Japonais")),
				new Noeud("Indo-Européenne", new Noeud("Latine", 
						new Noeud("Francais"), new Noeud("Espagnol")), new Noeud("Germanique")));
		
		// affichage
		System.out.println(arbre.sousArbre());
		
		// création de l'adaptateur, pour avoir un TreeModel sur l'arbre
		final TreeModel modele = new Adaptateur(arbre);
		
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
				// création et affichage de la fenetre avec un JTree affichant les
				// données de 'arbre'
				// NB : le JTree fait simplement référence au modèle
				JFrame fenetre = new JFrame("Test arbres");
				fenetre.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				fenetre.add(new JScrollPane(new JTree(modele)), BorderLayout.CENTER);
				fenetre.pack();
				fenetre.setVisible(true);
            }
        });
	}
}