IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

MDA par la pratique

MDA par la pratique


précédentsommairesuivant

4. Concepts avancés : finalisation de l'application

4.1. Création d'associations

4.1.1. Modélisation

Dans cette section nous allons apprendre comment créer des associations entre les entités. Nous allons commencer par modéliser une entité Timecard (une fiche de pointage) et nous créerons ensuite deux associations entre Timecard et Person. Le modèle complet ressemblera à ceci:

Création d'associations entre Timecard et Entity
Création d'associations entre Timecard et Entity

L'entité Timecard a trois attributs:

  1. status: une énumération avec quatre valeurs possibles.
  2. begDate: la date de début de la fiche, qui devrait tomber au début de la semaine.
  3. comments: les commentaires ajoutés par la personne qui soumet la fiche et/ou celle qui contresigne la fiche.

L'entité Timecard a également deux associations avec Person:

  1. submitter: la personne qui a soumis la fiche.
  2. approver: la personne qui approuvera la fiche.

Les flèches sur les deux associations indiquent qu'elles sont navigables depuis Timecard vers Person, ce qui signifie que la fiche référence à la fois la personne qui l'a remplie et celle qui l'approuve. Attention: une navigabilité dans les deux sens est représentée par une association sans aucune flèche. Le "1" et le "*" à chaque bout de l'association indiquent leur multiplicité. L'assocation du haut signifie qu'une personne peut soumettre plusieurs (signifié par "*") fiches. De la même façon, l'association du bas signifie qu'une même personne peut contresigner plusieurs fiches.

Voilà qui illustre parfaitement l'originalité de l'utilisation d'UML dans le cadre d'une approche MDA. En effet, si vous avez déjà utilisé UML dans un contexte de conception traditionnel, vous remarquerez que nous ajoutons à ces associations beaucoup plus d'informations ici. En fait, il faut bien voir que les noms donnés aux extrémités navigables (submitter et approver) vont servir de nom aux variables correspondantes dans la classe Timecard générée, que le signe "+" qui précède ces identifiants va faire que ces variables seront publiques, et que le fait que la multiplicité de ce côté soit de 1 va générer une référence unique au lieu d'une collection. Et même l'information de multiplicité du côté non-navigable est utile, car elle va permettre au générateur de configurer correctement le fichier de mapping Hibernate et par extension de générer un script DDL cohérent. Tout ça pour vous faire remarquer que nous commençons mine de rien à utiliser UML de façon beaucoup plus détaillée pour nous rapprocher progressivement du code généré. Rassurez-vous, bien souvent, si le générateur manque d'informations pour générer son code, le validateur de modèle vous en préviendra avec un message très significatif. Tiens, c'est étrange! Comme un compilateur le ferait avec du code... Un compilateur de modèle! Oh my god! ;o)

Nous sommes maintenant prêts à ajouter ces éléments à notre modèle TimeTracker. Pour ce faire, suivez les instructions de la section correspondant à votre outil de modélisation UML.

MagicDraw UML

Cette section vous fournira toutes les instructions dont vous aurez besoin pour créer l'entité Timecard ainsi que ces associations avec l'entité Person, grâce à Magicdraw UML. Voici le modèle que nous voulons obtenir à la fin de ce processus:

Modélisation de l'entité Timecard et de ses associations avec Person
Modélisation de l'entité Timecard et de ses associations avec Person
  • Dans le modèle TimeTracker, ouvrez le diagramme "Objets du domaine". Ce diagramme contient déjà l'entité Person.
  • Ajoutez maintenant la classe Timecard au diagramme et affectez-lui le stéréotype Entity.
  • Ensuite, nous ajoutons l'attribut status à Timecard. Remarquez que status est de type TimecardStatus, qui s'avère être une énumération. Nous devons donc d'abord définir TimecardStatus. Donc créez une classe appelée TimecardStatus et donnez-lui le stéréotype "Enumeration". Ajoutez quatre attributs à celle-ci. Ils représentent les valeurs de l'énumération. Maintenant que nous avons défini TimecardStatus, nous pouvons ajouter l'attribut status à Timecard comme montré ci-dessus.

A ce stade, pour peu que vous ayez appris à apprécier les énumérations type-safe apparues dans Java 5, je sens la colère monter en vous. Qu'est ce que c'est que cette classe pour modéliser une énumération, et ces variables membres pour simuler les littéraux? En plus il existe un stéréotype "enumeration" standard dans UML! Et vous serez encore plus choqué d'apprendre qu'on ne peut même pas modéliser des opérations dans cette énumération. Mais qu'est-ce que...? On se calme, et on n'oublie pas qu'AndroMDA ne génère pas encore de code qui utilise à fond les possibilités de Java 5. Et dans l'ancien temps, pour implémenter des énumérations en Java, on utilisait un design pattern qui servait à les émuler avec des classes. Et bien devinez quoi, c'est justement ce modèle de conception qui est mis en oeuvre par AndroMDA! C'est dingue, non? Notez enfin qu'il est toujours recommandé que les variables membres qui servent à émuler les littéraux d'une énumération soient de type String. Ensuite libre à vous de leur donner une valeur explicite. Si vous ne le faites pas, la valeur sera égale au nom du littéral.

  • Ajoutez ensuite les attributs begDate et comments à Timecard.
  • Nous devons maintenant modéliser une première association entre Timecard et Person. Pour ce faire, cliquez sur l'icône association dans la barre d'outils (13ème en partant du haut). Cliquez sur la classe Timecard, faites glisser jusqu'à la classe Person et relâchez la souris. Une nouvelle association est alors créée.
  • Cliquez droit sur l'extrémité de l'association du coté de Timecard et décochez "Navigable". Ainsi l'association sera à sens unique depuis Timecard vers Person.
  • Cliquez droit sur l'extrémité de l'association du côté de Person et sélectionnez la multiplicité 1. De la même manière, définissez la multiplicité de l'extrémité côté Timcard à plusieurs (*).
  • Cliquez droit sur l'extrémité de l'association du côté de Person et sélectionnez "Editer Nom". Entrez la spécification suivante: "+submitter". L'autre bout de l'association n'a pas besoin d'être nommé puisqu'il n'est pas navigable. Voilà qui termine l'association submitter depuis Timecard vers Person.
  • Sur le même modèle, ajoutez une association approver depuis Timecard vers Person. Déplacez les associations vers le haut ou vers le bas de façon à ce qu'elles ne se chevauchent pas.
  • Assurez-vous que votre diagramme de classes ressemble à celui qui apparaît ci-dessus.
  • Enregistrez le projet.

Nous allons maintenant pouvoir générer le code pour Timecard. Pour ce faire, rendez-vous au paragraphe 4.1.2.

4.1.2. Génération de code

Suivez les instructions suivantes pour générer le code.

  1. Ouvrez une fenêtre de commandes et rendez-vous dans le répertoire C:\timetracker .
  2. Exécutez la commande "maven -o clean install". La cible "clean" nous permet de nettoyer les dossiers target avant de régénérer le code. Assurez-vous que vous obtenez un message BUILD SUCCESSFUL quand Maven termine son exécution.

Ouvrez le dossier C:/timetracker/core/target/src/org/andromda/timetracker/domain dans l'explorateur Windows. Vous remarquerez la présence de 7 nouveaux fichiers dans ce dossier qui sont tous reliés à l'entité Timecard. Passez ces fichiers en revue. Remarquez que la classe Timecard contient deux références à la classe Person, mais que la classe Person ne contient aucune référence à la classe Timecard. Celà vient du fait que les deux associations sont à sens unique depuis Timecard vers Person.

Nous avons maintenant couvert les bases de la modélisation pour AndroMDA. Dans la prochaine section nous allons ajouter les fonctionnalités restantes au modèle de TimeTracker.

4.2. Complétion du modèle de TimeTracker

Dans cette section nous allons ajouter les fonctionnalités restantes au modèle de TimeTracker. Maintenant que vous comprenez la plupart des concepts importants de modélisation, nous ne vous donnerons des instructions détaillées que lorsque celà sera nécessaire. Alors remontez vos manches et allons-y.

4.2.1. Ajout des entités manquantes

Ouvrez le diagramme "Objets du Domaine" et ajoutez les détails manquants comme indiqué ci-dessous.

Modèle de données complet
Modèle de données complet
  • TimeAllocation représente la période pendant laquelle une personne a travaillé sur une tâche donnée. Cette période est spécifiée en utilisant une "valeur imbriquée" de type TimePeriod. Une valeur imbriquée est spécifiée en marquant la classe avec le stéréotype EmbeddedValue. Contrairement aux entités, les valeurs imbriquées n'ont pas d'identité séparée dans la base de données. Elles sont imbriquées dans les tables d'entité et sont considérées comme faisant partie intégrante de l'entité. Remarquez que vous devez créer la valeur imbriquée TimePeriod avant de pouvoir l'utiliser comme type dans la classe TimeAllocation.
  • Chaque Timecard peut contenir plusieur TimeAllocations. Une tâche peut également être associée avec plusieurs TimeAllocations.
  • Dans la première association, l'extrémité du côté de Timecard est marquée comme étant composite. Celà signifie qu'une TimeAllocation ne peut exister sans une Timecard.
  • Puisque TimeAllocation est connecté si étroitement avec Timecard, nous voudrions utiliser cette observation pour simplifier notre code. Lorsqu'une Timecard est enregistrée, toutes les allocations associées devraient être sauvegardées automatiquement. De la même façon, lorsqu'une allocation est supprimée d'une timecard, elle devrait être supprimée automatiquement de la base de données. Ceci est spécifié en affectant "all-delete-orphan" à la valeur balisée andromda.hibernate.cascade. Pour savoir comment modifier cette valeur, rendez-vous dans le paragraphe correspondant à votre outil de modélisation UML.

MagicDraw UML

  1. Cliquez droit sur la fin d'association appelée "allocations" et sélectionnez Spécification. Une boîte de dialogie de spécification de fin d'association s'ouvre.
  2. Sélectionnez l'onglet "Valeurs balisées".
  3. Sélectionnez la balise andromda.hibernate.cascade dans le panneau de gauche et cliquez sur "Ajouter valeur".
  4. Dans la zone de texte à droite, saisissez la valeur "all-delete-orphan" (sans les guillemets, à la place de la valeur par défaut "none") et cliquez sur OK. La nouvelle valeur balisée s'affiche dans le diagramme.

4.2.2. Ajout de comportements personnalisés aux entités

Jusqu'ici, nous nous sommes limités au comportement par défaut généré par AndroMDA pour les entités. Chaque entité est générée sous la forme d'un "Plain Old Java Object" (POJO) associé à un objet d'accès aux données (DAO). Ces deux objets bénéficient de comportements par défaut implémentés par des méthodes générées automatiquement. Voyons maintenant comment nous pouvons demander à AndroMDA de nous fournir des points d'entrée pour injecter des comportements personnalisés.

Créez une nouveau diagramme de classes sous le package "domain" et appelez-le "Objets de Domaine - Comportement". Il est préférable d'ajouter les détails comportementaux des classes dans un diagramme séparé. De cette façon, les diagrammes qui représentent les détails purement structurels, tels que le diagramme figurant ci-dessous, restent simples et faciles à comprendre. Ajoutez ensuite les méthodes affichées ci-dessous dans le nouveau diagramme que vous venez de créer. Puisque les entités Person et Timecard existent déjà dans votre modèle, elles ne doivent pas être recréées mais vous devez les faire glisser depuis l'explorateur de modèle (ou l'arbre de confinement) vers la fenêtre de diagramme. Ajoutez ensuite les méthodes aux deux entités.

Aspects comportementaux des entités
Aspects comportementaux des entités
  • La méthode findByUsername() retrouve une personne par son nom. Le signe "@" devant la méthode indique qu'il s'agit d'une requête. Les méthodes de requête sont utilisables lorsque vous essayez de trouver une ou plusieurs entités à partir de leurs attributs. La bonne nouvelle, c'est qu'AndroMDA est capable d'implémenter intégralement les méthodes de requêtes sans votre concours. Il va générer la requête Hibernate adéquate et renvoyer le résultat sous forme d'un objet ou d'une collection en fonction du type de retour que vous spécifiez.
  • Comme vous le savez, l'association entre Timecard et TimeAllocation est navigable dans les deux sens. Donc chaque objet doit maintenir une référence à l'autre. addTimeAllocation() est une méthode utilitaire qui fait exactement celà en un seul endroit. AndroMDA va générer une implémentation vide de cette méthode dans TimecardImpl. Et nous complèterons les détails.

MagicDraw permet de saisir les mêmes informations de façons différentes: boîtes de dialogues, panneaux de l'interface, mais aussi ce que j'appelle la syntaxe en ligne. Par exemple, pour spécifier une opération dans une classe, vous pouvez soit passer par l'arbre de confinement, soit par la boîte de spécification de la classe, mais vous pouvez aussi rédiger la spécification en ligne dans la classe, en saisissant ce que verrez une fois l'opération terminée. Par exemple, pour spécifier une opération "test" qui prend un paramètre "truc" de type String qui retourne un "void", il vous suffit de cliquer sur la classe, d'utiliser la séquence Ctrl+Alt+O sur le clavier pour ajouter une méthode, et de saisir la chaîne suivante: "test(truc:String):void". MagicDraw se charge ensuite d'interpréter cette chaîne et de placer les informations au bons endroits dans le modèle. Bien que très puissante, cette méthode est limitée aux cas simple. Ainsi, si vous essayez de saisir une méthode de type requête de cette manière, vous vous rendrez vous compte que le "@" en tête de méthode est mal interprété et que la méthode n'est pas identifiée comme une requête. Mon conseil: méfiez-vous de cette méthode pour les cas avancés, et ne vous fiez qu'aux boîtes de dialogue de spécification.

A présent, générez le code à partir de ce nouveau modèle en exécutant la commande "maven -o clean mda". Ouvrez le fichier PersonDaoBase.java sous core/target/src/org/andromda/timetracker/domain. Vous y trouverez la méthode findByUsername() complètement implémentée par AndroMDA. La requête Hibernate générée apparaît ci-dessous.

 
Sélectionnez
public Object findByUsername(final int transform, final java.lang.String username)
{
    return this.findByUsername(
		transform,
		"from org.andromda.timetracker.domain.Person as person where person.username = :username", 
		username
	);
}

Maintenant complétons l'implémentation de la méthode addTimeAllocation(). Ouvrez le fichier TimecardImpl.java sous core/src/java/org/andromda/timetracker/domain. Localisez l'implémentation par défaut de addTimeAllocation(), qui lance une UnsupportedOperationException pour l'instant, avec une balise @todo pour vous faciliter le repérage des méthodes à implémenter. Remplacez l'implémentation générée par celle qui figure ci-dessous. Celle-ci s'assure que Timecard et TimeAllocation ont toujours une référence l'une vers l'autre.

 
Sélectionnez
public void addTimeAllocation(org.andromda.timetracker.domain.TimeAllocation timeAllocation)
{
    getAllocations().add(timeAllocation);
    timeAllocation.setTimecard(this);
}

Assurez-vous que votre code compile. Tout ce que vous avez besoin de faire, c'est de construire le module core. Pour ce faire, exécutez simplement la commande "maven -o core".

4.2.3. Ajout des objets de valeur manquants

Ouvrez le diagramme "Objets de valeur" et ajoutez les détails restants pour arriver au résultat qui figure ci-dessous.

Ajout des objets de valeur manquants
Ajout des objets de valeur manquants

Il n'y a rien de vraiment nouveau ici, excepté le fait que nous fournissons deux vues de l'entité Timecard. TimecardSummaryVO est très légère et peut être utilisée pour afficher des listes de timecards. Quant à TimecardVO, elle est un peut plus lourde puisqu'elle inclut les TimeAllocations en plus des attributs de TimecardSummaryVO. Cet objet de valeur sera utilisé lorsque nous voudrons afficher les détails d'une timecard particulière. Notez que TimecardVO hérite de TimecardSummaryVO, héritant du même coup de tous ses attributs.

A présent vous pouvez générer le code pour votre modèle modifié en exécutant la commande "maven -o clean install". Assurez-vous que vous obtenez un message "BUILD SUCCESSFUL" à la fin de la construction.

4.2.4. Ajout du service TimeTrackerService

Nous allons maintenant ajouter au modèle le service TimeTrackerService. Ce service fournit toutes les opérations de suivi du temps qui pourraient être utilisées par une interface utilisateur. Ouvrez le diagramme "Services" et ajoutez-y TimeTrackerService pour obtenir le modèle qui figure ci-dessous.

Le service TimeTrackerService
Le service TimeTrackerService

A présent, générez le code pour le modèle modifié en exécutant la commande "maven -o clean install". Assurez-vous d'obtenir un message "BUILD SUCCESSFUL".

4.2.5. Création du schéma

Maintenant que la structure de nos entités est pratiquement finalisée, nous pouvons créer le schéma correspondant dans la base de données. Vous vous en souvenez peut-être, AndroMDA génère automatiquement le script de création de la base de données. Celui-ci se trouve dans C:/timetracker/core/target/schema-create.sql. Ouvrez ce fichier et passez son contenu en revue. Vous y trouverez les instructions SQL pour créer toutes les tables et les contraintes nécessaires. Pour créer le schéma, exécutez la commande "maven -o create-schema.

Notez que PostgreSQL n'apprécie pas spécialement la première instruction dans schema-create.sql ("drop sequence hibernate_sequence"). Nous vous recommandons donc de commenter cette ligne pour PostgreSQL.


précédentsommairesuivant