Cours BUT Info

TP 5 — Patrons de conception

Une fabrique Factory sur des fractions extends Number — avec une extension aux chaînes sans aucun if — puis un inventaire récursif du parc informatique, composite avant la lettre.

Objectif du TP : mettre en œuvre la version la plus simple du patron Factory pour créer des objets numériques de types variés, puis modéliser un inventaire de parc informatique où chaque élément peut en contenir d'autres — du Composite avant la lettre.

1. Fraction extends Number

Pour stocker n'importe quel élément numérique quel que soit son type, la structure s'appuie sur Number : la classe-mère commune à tous les types numériques, la plus basse dans la hiérarchie d'héritage. La classe Fraction (num, denom) en dérive et implémente les quatre conversions exigées par la classe abstraite :

public class Fraction extends Number {
    private int num;
    private int denom;
    public Fraction(int num, int denom) { /* ... */ }
    @Override public int intValue()       { /* ... */ }
    @Override public long longValue()     { /* ... */ }
    @Override public float floatValue()   { /* ... */ }
    @Override public double doubleValue() { /* ... */ }
    @Override public String toString()    { /* ... */ }
}

Une fois Fraction dérivée de Number, toute fraction est un Number et circule partout où un Number est attendu.

2. NumberFactory, la fabrique

« Nous allons créer une "fabrique" basée sur la version la plus simple du patron de conception Factory » : la fabrique instancie des objets de type Number à partir de valeurs de différents types — ou de deux entiers pour une fraction :

public class NumberFactory {
    public Number number(int value)          { /* ... */ }
    public Number number(long value)         { /* ... */ }
    public Number number(float value)        { /* ... */ }
    public Number number(double value)       { /* ... */ }
    public Number number(int num, int denom) { /* une Fraction */ }
}

C'est la version légère du cours : une classe dont les méthodes décident du type concret créé, et retournent toujours le type de la classe mère.

3. Tabular et le scénario

La classe Tabular profite de la fabrique : elle stocke size éléments quelconques de type Number — Tabular(int size) crée la structure, set(int i, Number number) affecte l'emplacement i, max() identifie et retourne la valeur maximale, toString() affiche le contenu. Pour comparer des Number hétérogènes — entiers, réels, fractions — le seul langage commun est la vue double : la comparaison de max() passe naturellement par doubleValue(). Q4 — écrivez un scénario qui mélange des fractions et d'autres nombres dans un même Tabular et en affiche le maximum : la preuve que la fabrique et le polymorphisme fonctionnent ensemble.

4. L'extension aux chaînes, sans aucun if

Q5 — étendez la fabrique pour qu'elle accepte aussi des chaînes de caractères, en n'utilisant aucune structure conditionnelle : uniquement la gestion des exceptions, en cascade. Chaque type candidat tente sa conversion dans son propre try ; l'échec (NumberFormatException) fait passer au suivant :

public Number number(String s) {
    try { return number(Integer.parseInt(s)); }
    catch (NumberFormatException e) { /* pas un entier : on tente autre chose */ }
    try { return number(Float.parseFloat(s)); }
    catch (NumberFormatException e) { /* etc. pour les autres types */ }
    // ... dernier essai, ou échec final
}

Q6 — modifiez le scénario pour illustrer la nouvelle fonctionnalité. Q7 — commit : « tpQU-05::exo-pattern », puis poussez.

L'exception à la place du test

C'est le même esprit que le multi-catch du chapitre 4 : on tente, et c'est l'échec qui oriente le flot d'exécution. Lisibilité gagnée ; en contrepartie, lever une exception coûte cher — acceptable ici, à éviter dans une boucle serrée.

5. Le parc informatique en composite

Objectif : un inventaire des éléments qui composent les postes de travail d'un parc. La constitution modulaire des ordinateurs permet montage, échange, remplacement des composants ; l'inventaire requiert donc la mémorisation des assemblages. Trois types se partagent le travail :

  • Piece : un composant indivisible tel qu'acheté dans le commerce — il porte un numéro d'inventaire ;
  • Element : un assemblage d'une ou plusieurs pièces ou éléments — la définition est récursive, un Element peut donc en contenir d'autres ;
  • Machine : l'ensemble des composants du poste de travail d'une personne.

Fonctionnalités attendues : créer un élément, en ajouter, en enlever ; associer un poste de travail à un utilisateur ; identifier une pièce et connaître son numéro d'inventaire. Le cœur du diagramme UML tient en une ligne : l'attribut #components de Element est une agrégation 0..* vers Piece et Element — l'auto-référence qui fait tout le patron. La description du parc devient une arborescence, et l'affichage du poste d'Alice s'obtient par un simple parcours récursif : chaque niveau s'affiche lui-même puis délègue à ses composants.

Poste de Alice:
  écran(N.1340)
  clavier(N.1934)
  souris(N.2805)
  unité centrale:
    carte mère(N.1569)
    extension mémoire(N.289)
    disque scsi(N.299)
    carte graphique:
      extension mémoire(N.2879)
      processeur graphique(N.7289)
  webcam(N.2222)

Notez l'imbrication : la « carte graphique » est elle-même un Element dans l'« unité centrale ». Q1 — écrivez Element, Machine et Piece ; Q2 — écrivez la classe UseStock réalisant le scénario qui mène à l'affichage ci-dessus ; Q3 — commit : « tpQU-05::exo-machine », puis poussez.

Reconnaître un Composite

Dès qu'un objet peut contenir des objets de son propre type — fichiers dans des répertoires, sous-menus dans des menus, articles dans des paniers — le Composite est candidat. Ici, Element contient des Element : le parcours récursif remplace une cascade de tests de types.


tpQU_05.pdf

R2.03 · TP 5 — Patrons de conception · 2 pages

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

Chargement du document…

Dernière mise à jour le

Sur cette page