CentraleSupélecDépartement informatique
Plateau de Moulon
3 rue Joliot-Curie
F-91192 Gif-sur-Yvette cedex
Génie logiciel orienté objet - Exercice : Modéliser avec UML, programmer avec Java

Exercice

L'objectif de cet exercice est de montrer comment on peut concevoir un logiciel avec UML et le programmer en Java en utilisant simultanément deux outils (ici, Modelio et Eclipse).

Cette démarche devra être appliquée pour le projet intégré.


Vu sa faible complexité, le sujet de cet exercice (représentation de polynômes) ne nécessite pas nécessairement une démarche préalable de modélisation. Pour autant, celle-ci sera effectuée sur cet exemple simple afin de maitriser la démarche et ainsi être en mesure de l'appliquer pour des cas plus complexes.

1. Modèle UML initial

On souhaite représenter les nombres rationnels, c'est-à-dire les nombres qui s'écrivent sous la forme

{$$ numérateur \over denominateur $$}

où le numérateur et le dénominateur sont entiers. Un nombre rationnel sera représenté par la donnée de son numérateur et de son dénominateur.

  • Lancer Modelio et créer un projet PolynomeUML.

Modelio a créé un automatiquement un paquetage polynomeuml dans le projet PolynomeUML, mais celui-ci sera ignoré lors de la génération du code Java.

  • Créer un paquetage gloo dans le paquetage polynomeuml, puis un paquetage polynome dans ce paquetage gloo.

Figure 1. Création des paquetages


  • Créer un diagramme de classes dans le dernier paquetage créé.
  • Créer une classe Rational sur ce diagramme, lui ajouter deux attributs privés, numerator et denominator de type integer.
  • Créer une classe RationalTest sur ce diagramme, lui ajouter une méthode statique (propriété Class) main ayant un paramètre args de type string et de multiplicité 0..* (double-clic sur la méthode pour pouvoir ajouter des paramètres).

Figure 2. Les deux classes UML


2. Traduction du modèle UML en Java

  • Dans le menu Configuration, choisir Modules…, cliquer sur Add…, sélectionner JavaDesigner 5.4.02 :


  • Cliquer sur Deploy in the project, puis sur Close.
  • Sélectionner successivement les 2 paquetage gloo et polynome et les deux classes Rational et RationalTest dans la vue modèle, et activer pour chacun la propriété Java element dans l'onglet Java :


L'icone Java est alors ajouté aux éléments visibles :



  • Utiliser le menu contextuel sur le paquetage gloo et demander la génération de code dans le sous-menu Java Designer :


3. Création du projet Eclipse correspondant

  • Lancer Eclipse, demander la création d'un projet Java gloo.polynome, décocher la case Use default location, cliquer sur Browse… et sélectionner le dossier correspondant à votre projet PolynomeUML (dans /config/modelio/workspace) :


  • Décocher la case Create module-info.java


  • Cliquer sur Next, sélectionner l'onglet Librairies, sélectionner Classpath dans la vue centrale puis Add External JARs…


  • Choisir Other LocationsComputerusrlibmodelio-open-source5.4bundlejavajavadesigner.jar, cliquer sur Open


  • Cliquer sur Finish.

Le code généré contient des annotations @objid, vous ne devez pas les modifier, elles sont utilisées par Modelio quand il relit le code. Il y a une telle annotation devant chaque élément Java généré (classe, attribut, méthode…) : vous pouvez déplacer cet élément pour réorganiser votre code, mais vous devez déplacer l'annotation en même temps.



Vous pouvez adapter la génération du code Java à vos besoins et contraintes ; par exemple, le type de l'argument de main est List<String> au lieu de String[], il faut corriger.

  • Dans Modelio, sélectionner l'argument de main dans la vue modèle, et choisir Array comme Collection to use. Régénérer le code Java et vérifier la bonne prise en compte de cette modification (il faut éventuellement demander à Eclipse de rafraichir ses données FileRefresh).


4. Synchronisation du modèle UML avec le code Java

La modélisation UML est utile pour trouver les bonnes classes et les opérations nécessaires au fonctionnement de l'application. Elle n'est pas pertinente pour d'autres tâches, par exemple l'écriture des constructeurs, pour lesquelles un IDE comme Eclipse sera beaucoup plus efficace. Pour autant, il faut veiller à conserver la cohérence entre le monde UML et le monde Java.

  • Dans Eclipse, ouvrez le fichier Rational.java et positionner le curseur à l'intérieur de la classe.
  • Dans le menu Source, choisir Generate Constructor using Fields… :


  • Les 2 attributs sont sélectionnés par défaut, cliquer sur Generate, le constructeur est automatiquement écrit (l'appel super() sera expliqué lors du cours sur l'héritage).


  • N'oublier pas de sauvegarder les changements apportés au fichier Rational.java.

Pour ne pas perdre le code généré par Eclipse, il faut que Modelio soit capable de relire ce code afin de le restituer lors de la prochaine génération. La version de Java utilisée doit être prise en compte lors de cette opération, sachant que Modelio n'est capable de relire que du code Java 8.

  • Pour configurer Modelio afin qu'il puisse relire correctement du code Java, choisir dans le menu contextuel sur le paquetage polynomeuml Java DesignerConfigurationEdit accessible classes :


  • Sélectionner la ligne rt.jar existante et supprimer là avec le croix rouge ❌
  • Cliquer sur l'icône à gauche de la croix rouge ❌ puis naviguer vers Other LocationsComputerusrlibjvmjava-8-openjdk-amd64jrelib, sélectionner rt.jar et cliquer sur Open, puis sur OK :


  • Choisir dans le menu contextuel sur le paquetage polynomeuml Java DesignerUpdate model from source if necessary :


Le constructeur n'apparait pas sur le diagramme de classes, mais on peut vérifier sa présence dans l'onglet modèle :



Il est possible de le glisser-déposer sur la classe Rational du diagramme de classes pour le rendre visible.

En sens inverse, Modelio peut aussi générer du code utilitaire :

  • Sélectionner l'attribut numerator et, dans l'onglet Java, cocher la case Getter :


  • Faire la même chose pour l'attribut denominator, les deux opérations apparaissent sur le diagramme de classes, et sont présentes dans le code Java après une nouvelle génération :


Il est important, pour conserver la synchronisation entre le modèle UML de votre projet et son code Java, de respecter ces procédures :

  • Après une modification dans Eclipse (ne pas oublier de sauvegarder le ou les fichiers modifiés), utiliser Update model from source if necessary dans Modelio.
  • Après une mise à jour du modèle dans Modelio, générer de nouveau le code Java et utiliser si besoin Refresh sous Eclipse.


5. Affichage d'un rationnel

  • Sous Eclipse, compléter la méthode main avec le code suivant :
    public static void main(String[] args) {
        Rational q = new Rational(3, 4);
        System.out.println("q = " + q);
        Rational r = new Rational(1, 6);
        System.out.println("r = " + r);
    }
  • Exécuter votre classe de test. L'affichage obtenu est-il satisfaisant ? Pourquoi ?
  • Ajouter à la classe Rational une méthode toString (vous pouvez demander à Eclipse de générer une version via le menu SourceGenerate toString…). Les chaînes renvoyées par toString devront être de la forme 7/5, 0/4, 2/6, 3/1, etc.
  • Tester à nouveau le programme.

Pour la méthode main donnée précédemment, on doit obtenir l'affichage suivant :

q = 3/4
r = 1/6
  • Mettre à jour votre modèle UML.

6. Monôme

Nous souhaitons maintenant manipuler des polynômes à coefficients rationnels. On considère qu'un polynôme est défini par un ensemble de monômes. Nous commençons par définir une classe Monomial, puis une classe Polynomial.

Un monôme est défini par un coefficient (qui est de type rationnel) et un degré (de type entier, et obligatoirement positif ou nul).

  • Dans Modelio, créer une classe Monomial ayant un attribut degree privé et de type entier et une composition navigable vers Rational ; l'extrémité correspondante sera privée, aura une multiplicité de 1 et pour nom coefficient. Créer une classe MonomialTest avec une méthode main.
  • Générer le code Java, définir dans Eclipse le constructeur de Monomial et la méthode toString donnant une représentation textuelle du monôme (par exemple : 3/4*X^5 ; on ne cherchera pas à traiter les cas particuliers, ainsi on écrira : 1/1*X^0).
  • Compléter la méthode main de MonomialTest avec le code suivant :
    public static void main(String[] args) {
        Monomial m = new Monomial(
            new Rational( 3, 4 ),
            5
        );
        System.out.println( "3/4*X^5 = " + m );
    }
  • Vérifier que vous obtenez l'affichage suivant :
3/4*X^5 = 3/4*X^5
  • Mettre à jour votre modèle UML.
  • Ajouter dans Modelio des accesseurs au coefficient et au degré du monôme.

7. Polynôme

  • Dans Modelio, créer une classe Polynomial ayant une composition navigable vers Monomial ; l'extrémité correspondante sera privée, aura une multiplicité * et pour nom monomials. Créer une classe PolynomialTest avec une méthode main.

On appelle forme canonique d'un polynôme une écriture dans laquelle les monômes sont triés par degrés décroissants, et où il n'y a qu'un seul monôme par degré (et aucun de coefficient nul).

Nous allons faire en sorte que la représentation du polynôme soit directement sous forme canonique. On peut pour cela stocker les monômes sous la forme d'un dictionnaire utilisant comme clef le degré et comme valeur associée le monôme correspondant (le coefficient aurait suffi, mais l'affichage sera plus facile en mémorisant le monôme). Pour accéder de plus aux monômes dans l'ordre donné par les degrés, nous n'utiliserons pas une HashMap présentée en cours, mais une TreeMap (n'hésitez pas à aller consulter la documentation de cette classe).

Exemple de code utilisant TreeMap :

// Un TreeMap est un dictionnaire dont l'implémentation est basée sur un arbre
// des clefs permettant un parcours de l'arbre selon l'ordre croissant des clefs.
TreeMap<Integer, String> m = new TreeMap<>();
m.put(3, "trois");
m.put(1, "un");
m.put(2, "deux");
// Une entrée d'un dictionnaire contient la clef et la valeur associée
for (var entry : m.entrySet()) {
    System.out.println(entry.getKey() + " = " + entry.getValue()); 
}
/* Affichage obtenu :
1 = un
2 = deux
3 = trois
*/
  • Sous Modelio, demander que la génération Java de l'extrémité monomials se fasse avec comme collection un TreeMap as SortedMap dont la clef sera un Integer, générer le code Java, vérifier avec Eclipse que vous obtenez dans la classe Polynomial :
    private SortedMap<Integer, Monomial> monomials = new TreeMap<Integer, Monomial> ();
  • Mettre à jour le modèle UML (la clef va apparaître sur le diagramme).
  • Nous aurons besoin de faire des sommes de rationnels. Dans Modelio, ajouter à la classe Rational une méthode add qui rend un nouveau rationnel (et donc ne modifie pas le rationnel courant) égal à la somme (non simplifiée) du rationnel courant et d'un autre rationnel passé en paramètre.
  • La création d'un polynôme se fera en ajoutant des monômes à un polynôme initialement vide. Ajouter à la classe Polynomial une méthode add qui reçoit un monôme en paramètre et l'ajoute au polynôme courant.
  • Générer le code Java, le compléter sous Eclipse, ajouter une méthode toString à Polynomial et des tests de création et d'affichage de polynômes. N'oubliez pas de mettre à jour votre modèle UML après les modifications faites sous Eclipse.

8. Calculs

Nous voulons maintenant évaluer un polynôme pour une valeur donnée de la variable (un rationnel).

  • Ajouter les comportements nécessaires dans les différentes classes. Vous aurez en particulier besoin de faire des multiplications de rationnels.


© 2024-25 CentraleSupélec