Introduction▲
Objecteering 6 est un outil de modélisation UML 2.0. Objecteering 6 permet l'analyse et la conception jusqu'à la génération de code, tests et déploiement d'application de notre projet. Cet outil offre aussi de nombreux modules (MDA) à déployer selon les besoins du projet. La modélisation est facilitée, les documents et le code restent conformes aux modèles produits et tout est automatisé ce qui permet un gain de temps évident.
Nous allons découvrir Objecteering 6 Enterprise Edition à travers des exemples simples permettant de parcourir la conception d'un diagramme de classe, la génération de documentation et la génération de code JAVA.
Afin de mieux appréhender cet outil, nous allons en premier lieu le découvrir à travers son interface, ses menus, etc. Puis, nous allons à travers un exemple simple réaliser un diagramme de classe, générer sa documentation associée et voir comment le code JAVA est généré ainsi que les modifications possibles à lui apporter.
I. À la découverte d'Objecteering 6 Enterprise Edition▲
I-A. Prérequis▲
Dans notre cas, nous utilisons la version 6.1 d'Objecterring sur une plateforme Windows XP. Cependant, d'après la documentation fournie, Objecteering 6 Enterprise Edition peut être utilisé sur les plateformes suivantes :
- Windows 2000 SP4 (x86) ;
- Windows XP SP1/SP2 (x86) ;
- Linux Red Hat 9 (x86) ;
- Linux Red Hat Enterprise 4.2.
Bien entendu, les connaissances de l'UML ainsi que du langage JAVA sont considérées comme acquises.
Il est possible de télécharger Objecteering 6 sur le site officiel Objecteering.
I-B. Nos premiers pas avec Objecteering 6 Enterprise Edition▲
Afin de découvrir l'environnement Objecteering 6, nous allons créer un projet (File>New project) :
Une fois le projet créé, nous accédons à l'interface de l'outil :
Nous avons donc à notre disposition un menu, une barre d'outils et diverses fenêtres de navigation ou configuration d'éléments.
- Le menu
Concernant les onglets fichier, edit et view, ils restent des fonctionnalités standards de n'importe quel outil.
L'onglet extension permet de préciser l'utilisation de pattern (que vous pouvez choisir dans une liste) selon vos choix de modélisation.
L'onglet tools permet aussi de créer des profils (de diagrammes ainsi que leurs composants) et choisir les couleurs de fond, la police, etc. De plus, il permet de déployer les modules MDAC qui nous intéressent, par exemple les modules nécessaires afin de générer du SQL, JAVA, Fortran, le module nécessaire à la documentation, et bien d'autres.
- La fenêtre UML explore r
Cette fenêtre est non seulement un explorateur de projet, mais elle permet aussi d'associer à notre projet les différents diagrammes ou éléments que nous souhaitons construire comme la documentation, le code généré ou encore des flux de données, etc.
Notons que lorsqu'une flèche noire est présente sur un élément, celle-ci permet de faire défiler une suite d'autres éléments du même type (rester 2 secondes l'icône enfoncée).
- La fenêtre principale
Cette fenêtre permet tout simplement le travail en cours de réalisation.
- La fenêtre auxiliaire
Cette fenêtre permet d'associer certaines options complémentaires au composant sélectionné.
- La fenêtre de sortie
Cette fenêtre permet de voir toutes les actions réalisées. De plus, elle s'avère très utile lors de génération automatique (en cas de problème) ou lors d'erreurs quelconques.
Marche à suivre si votre projet est verrouillé (locked) :
Si à un moment, Objecteering nous affiche la fenêtre ci-dessous c'est que notre projet est verrouillé.
Pour le déverrouiller, soit il nous suffit de cliquer sur le bouton yes, soit parfois il faut suivre une autre démarche. Dans le répertoire Bin de l'installation d'objecterring, nous disposons d'un outil appelé admtools.exe.
Il nous faut l'ouvrir, sélectionner le projet .ofp à déverrouiller puis cliquer sur le bouton « unlock a project » :
II. Exemple de projet et manipulation d'Objecteering 6 Enterprise Edition▲
Afin d'apprendre à manipuler cet outil, nous allons nous baser sur un exemple simple que voici.
Supposons qu'un client navigue sur un site commercial et dispose d'un panier d'articles. Nous souhaitons permettre au client d'ajouter un article, de supprimer un article, de visionner l'historique de mouvement de son panier ainsi que son total. Le client se caractérise par un numéro, un nom, un prénom et une adresse.
II-A. Conception de notre diagramme de classe▲
Pour ajouter un diagramme de classe à notre projet, nous cliquons sur l'icône .
En suivant notre exemple, nous réalisons trois classes en cliquant sur l'icône . Nos classes sont Client, Mouvement et Interface panier.
Nous verrons la construction de la classe Client ainsi que les liens possibles avec d'autres classes (le reste étant répétitif tout en respectant les contraintes du projet).
Nous créons donc notre classe Client :
Dans les propriétés de notre classe, nous pouvons spécifier bien entendu le nom de la classe (ici Client) ainsi que sa visibilité et son type. Nous déposons d'autres onglets comme Notes qui permet entre autres d'ajouter des commentaires concernant la classe, Tagged Values et Stereotype.
Dans l'onglet Notes, nous disposons de diverses possibilités applicables à notre classe :
II-A-1. Les attributs▲
Ensuite, nous ajoutons les attributs de classe de notre classe Client en cliquant sur l'icône et en nous positionnant sur notre classe.
Concernant un attribut, nous pouvons lui spécifier un nom, un type (les types UML 2.0 à savoir String, Integer, Boolean, etc.), une taille, sa visibilité et son mode d'accès. Le mode d'accès étant utile par la suite lorsque l'on souhaite que des accesseurs soient générés automatiquement dans le code. Nous retrouvons de même les onglets décrits auparavant avec les mêmes fonctions.
Remarque : par défaut, nous ne voyons pas la visibilité sur notre diagramme. Pour l'afficher pour nos classes, attributs et opérations, nous devons faire clic droit>Representation options>Show visibility :
De cette manière, nous passons de la classe Client ci-dessus, à la classe Client identique ci-dessous, mais avec les visibilités de classe et attributs (et méthodes pas encore réalisées) affichées :
II-A-2. Les opérations▲
Passons à l'ajout de méthode pour notre classe Client. Pour cela, faire comme l'ajout d'attribut en utilisant l'icône .
La première opération que nous allons ajouter est le constructeur de classe. Après avoir cliqué sur l'icône d'ajout d'opération, une fenêtre nous propose de nommer l'opération. Dans ce cas, celle-ci porte le nom de la classe. Enfin, pour spécifier que cette opération est un constructeur, il nous faut cocher la spécificité « create » comme suit :
Il est possible de distinguer une fois créés les constructeurs par une icône verte présentant un « C() » dans notre explorateur à gauche.
Pour l'ajout de paramètres d'entrée, il nous faut cliquer sur l'icône et pour les paramètres de retour l'icône . Nous ajoutons donc les paramètres d'entrée à notre constructeur :
Exemple de paramètres de retour (afin de voir la syntaxe sur notre diagramme de classe) :
Remarque : notons qu'il est bien entendu possible d'ajouter des paramètres ayant pour type une classe (Client apparaît dans notre liste de types).
Pour afficher sur votre diagramme les spécificités de certaines opérations comme les constructeurs, comme pour l'affichage des visibilités, nous effectuons un clic droit>Representation options>Stereotype representation>Text comme suit :
Il s'affiche alors sur chaque constructeur « create ».
Pour une opération « classique », c'est le même principe, mis à part qu'il ne faut pas cocher la case « create ». Nous ajoutons donc de cette manière les opérations ajouterArticle(integer prixArticle) et supprimerArticle(integer prixArticle).
Dernière remarque concernant les opérations et passage de paramètres, pour passer un paramètre de type object de classe, il nous suffit d'ajouter un paramètre d'entrée comme décrit précédemment, mais cette fois de sélectionner la classe qui nous intéresse :
Ce qui donne ceci dans notre diagramme de classe :
II-A-3. Les relations et multiplicités▲
Concernant les relations, Objecteering propose le panel suivant, sachant que certaines se déclinent en d'autres relations :
relation d'héritage |
|
Notre petit projet ne comporte que des associations, mais cela reste le même principe pour tous les autres types de relations. Nous construisons donc une association entre notre classe Client et notre classe HistoriqueMouvement du panier. Pour cela, nous utilisons l'icône . Nous lions donc les deux classes de Client vers HistoriqueMouvement, car c'est le client qui a besoin des services de cette classe et non l'inverse (de plus nous verrons qu'une fois le code généré, le sens a une réelle importance). Enfin, nous spécifions les multiplicités, les rôles ainsi que le nom de l'association en double cliquant sur le lien créé :
Notre relation une fois créée correspond à ceci :
Voici donc notre diagramme de classe complet après modélisation :
Génération de la documentation▲
Pour générer une documentation, il nous faut dans un premier temps déployer le module adéquat. Pour cela, nous cliquons sur l'onglet Tools du menu et nous choisissons Deploy an MDAC :
Ensuite, nous entrons dans le répertoire générator :
Pour enfin choisir notre module documentation à déployer :
Comme nous pouvons le constater, il y a de nombreux autres modules, dont le module JavaDeveloper que nous allons utiliser un peu plus tard. Après avoir déployé un module en général, une nouvelle icône apparaît alors dans notre menu à gauche de l'explorateur UML. Pour la documentation, c'est un livre bleu :
Nous pouvons passer à la création et génération de notre documentation. Pour cela, un clic sur l'icône documentation et une fenêtre avec divers champs à remplir apparaît :
Comme nous pouvons le constater, plusieurs formats de génération de documentation sont possibles, nous allons choisir le format HTML (dans ce cas, créer un répertoire pour la documentation, car le nombre de fichiers est important). Ensuite, nous générons la documentation en cliquant droit sur notre documentation créée en suivant ce chemin :
Pour réaliser une documentation en français, ne pas oublier de choisir Analysis.fr. De plus, à chaque modification, la documentation est à régénérer.
Exemple de documentation générée afin de se faire une idée.
Génération de code JAVA▲
Dans cette partie, nous verrons comment générer notre code Java de manière automatique, mais nous verrons aussi comment le compléter (corps de méthode, gestion d'exceptions ou encore import à ajouter) afin de générer un code complet compilable et exécutable directement d'Objecteering.
II-B-1. Première génération de code Java▲
Notre génération de code se fera dans le langage JAVA. Pour cela, nous devons déployer comme précédemment le module nécessaire : Tools> Deploy an MDAC>Generators>JavaDeveloper_v4.1.35.mdac. Une fois déployé, ce module nous donne accès à une nouvelle icône dans notre barre d'outils de l'explorer UML . Nous cliquons sur cette icône afin de spécifier le chemin de destination des fichiers java générés :
Ensuite nous lançons la génération de code java de cette manière :
Voici en guise d'exemple, le fichier Client.java qui a été généré automatiquement :
import
com.softeam.objecteering.javadeveloper.annotations.objingid;
import
java.util.List;
import
java.util.ArrayList;
@objingid
(
"862453768:9"
)
public
class
Client
{
@objingid
(
"862453768:12"
)
private
int
numClient;
public
int
getNumClient (
) {
return
this
.numClient;
}
public
void
setNumClient (
int
value) {
this
.numClient =
value;
}
@objingid
(
"862453768:17"
)
private
String nomClient;
public
String getNomClient (
) {
return
this
.nomClient;
}
public
void
setNomClient (
String value) {
this
.nomClient =
value;
}
@objingid
(
"862453768:20"
)
private
String adresseClient;
public
String getAdresseClient (
) {
return
this
.adresseClient;
}
public
void
setAdresseClient (
String value) {
this
.adresseClient =
value;
}
@objingid
(
"862453768:22"
)
private
String prenomClient;
public
String getPrenomClient (
) {
return
this
.prenomClient;
}
public
void
setPrenomClient (
String value) {
this
.prenomClient =
value;
}
@objingid
(
"862453768:26"
)
private
int
soldePanier =
0
;
public
int
getSoldePanier (
) {
return
this
.soldePanier;
}
public
void
setSoldePanier (
int
value) {
this
.soldePanier =
value;
}
@objingid
(
"862453768:67"
)
public
List<
HistoriqueMouvement>
historiqueMouvement =
new
ArrayList<
HistoriqueMouvement>
(
);
public
List<
HistoriqueMouvement>
getHistoriqueMouvement (
) {
return
this
.historiqueMouvement;
}
@objingid
(
"862453768:28"
)
public
Client
(
int
numClient,
String nomClient,
String prenomClient,
String adresse,
int
solde)
{
}
@objingid
(
"862453768:35"
)
public
void
ajouterArticle
(
int
prixArticle)
{
}
@objingid
(
"862453768:37"
)
public
void
supprimerArticle
(
int
prixArticle)
{
}
}
Regardons de plus près ce code généré. Nous pouvons y voir des balises de type @objingid (« 862453768:9 »), celles-ci sont utiles sous Eclipse avec l'ajout d'une librairie bien spécifique. Mais pour compiler et exécuter ce code (sachant que personnellement je ne les utilise pas), je n'ai rien trouvé d'autre que toutes les supprimer.
Ensuite, nous pouvons voir que tous nos attributs spécifiés dans notre diagramme de classe sont déclarés ainsi que nos méthodes. Les accesseurs générés diffèrent selon le mode d'accès choisi lors de la création d'un attribut (Read, Write, ReadWrite).
Enfin, regardons de plus près cette ligne de code :
public
List<
HistoriqueMouvement>
historiqueMouvement =
new
ArrayList<
HistoriqueMouvement>
(
);
Cette ligne a été générée, car nous avions mis une relation d'association de Client vers HistoriqueMouvement. De cette manière, une ArrayList du type HistoriqueMouvement a été créée dans notre classe Client afin de disposer des services de la classe HistoriqueMouvement. Enfin, nous pouvons voir que les méthodes de classe que nous avions créées dans notre diagramme de classe sont déclarées, mais vides de code. Nous allons donc voir comment remédier à cela et compléter notre génération afin que celle-ci soit complète.
II-B-2. Ajout des corps de méthodes, contraintes d'exception et import▲
Nous allons voir dans un même temps l'ajout d'exception et les imports Java. À cette étape, tout se passe dans la fenêtre auxiliaire. Tous les affichages peuvent générer des exceptions de type IOException, c'est pourquoi nous allons ajouter cette exception pour chaque méthode de la classe AffichagePanier ainsi que l'import java.io.* à cette même classe. En ce qui concerne les exceptions, il nous faut sélectionner une méthode, puis nous rendre dans l'onglet java de notre fenêtre auxiliaire. Nous pouvons y voir un champ Throws exception. C'est la que nous allons saisir toutes les exceptions que nous souhaitons ajouter à notre méthode (cette action est à réaliser pour chaque méthode) :
Pour ajouter plusieurs types d'exception, il suffit de les séparer par une virgule.
Ensuite, nous allons ajouter un import à notre classe Affichage Panier comme suit.
Il nous suffit de remplir le champ import. Voyons un petit extrait de code généré de la classe AffichagePanier afin de voir les modifications apportées :
import
com.softeam.objecteering.javadeveloper.annotations.objingid;
import
java.util.List;
import
java.util.ArrayList;
import
java.io.*;
@objingid
(
"862453768:11"
)
public
class
AffichagePanier
{
@objingid
(
"862453768:70"
)
public
List<
Client>
client =
new
ArrayList<
Client>
(
);
public
List<
Client>
getClient (
) {
return
this
.client;
}
@objingid
(
"862453768:40"
)
public
AffichagePanier
(
)
{
}
@objingid
(
"862453768:41"
)
public
int
choixAction
(
)
throws
IOException
{
}
@objingid
(
"862453768:43"
)
public
int
choixArticle
(
)
throws
IOException
{
}
Remarque : les imports java.util.List et java.util.ArrayList ont été générés automatiquement avant nos modifications du fait de la création de la collection d'objets client (relation entre AffichagePanier et Client).
Passons au corps de nos méthodes. Le moyen n'est pas du plus adéquat, car il n'y a pas de coloration syntaxique ni une quelconque aide pour le développeur. En effet, pour implémenter le corps d'une méthode, par exemple le constructeur de la classe Client, nous cliquons sur celle-ci puis nous utiliserons l'onglet implémentation puis le bouton add. Nous spécifions le type JavaCode et nous pouvons saisir le corps de notre méthode dans la fenêtre suivante avant de le valider :
Une fois l'implémentation de la méthode effectuée, nous pouvons voir que celle-ci reste visible dans notre onglet implémentation :
Il est possible de saisir le code d'une méthode d'une autre manière, mais beaucoup moins pratique. Pour cela il faut se rendre dans la fenêtre auxiliaire, onglet java, nous disposons d'un champ spécifique comme suit (personnellement je ne m'en sers jamais à part pour avoir un bref aperçu) :
Enfin, dans notre cas, afin de tester notre petit programme, nous avons ajouter une méthode main. Pour cela, nous avons sélectionné la classe AffichagePanier puis dans la fenêtre auxiliaire, onglet java, il nous suffit de cocher la case « main » :
Remarque : lors de l'ajout de corps de méthode, il ne faut pas ajouter les { et } d'ouverture de méthode et fermeture de méthode. Par contre, en cas de block conditionnel ou autres dans le corps de la méthode, il nous faut les placer.
Regardons notre classe AffichagePanier dans son intégralité après avoir ajouté chaque corps de méthode, un main, les exceptions et import souhaités et bien entendu régénérés le code java.
import
com.softeam.objecteering.javadeveloper.annotations.objingid;
import
java.util.List;
import
java.util.ArrayList;
import
java.io.*;
@objingid
(
"862453768:11"
)
public
class
AffichagePanier
{
@objingid
(
"862453768:70"
)
public
List<
Client>
client =
new
ArrayList<
Client>
(
);
public
List<
Client>
getClient (
) {
return
this
.client;
}
@objingid
(
"862453768:40"
)
public
AffichagePanier
(
)
{
}
@objingid
(
"862453768:41"
)
public
int
choixAction
(
)
throws
IOException
{
int
op;
System.out.println
(
"Entrez le type d'opération,Ajouter article: 1 sinon Supprimer Article:2"
);
String read =
null
;
boolean
ok =
false
;
do
{
try
{
InputStreamReader lec =
new
InputStreamReader (
System.in) ;
BufferedReader entree =
new
BufferedReader (
lec) ;
read =
entree.readLine
(
) ;
ok =
true
;
}
catch
(
IOException err){
ok =
false
;
}
}
while
(!
ok);
op=
Integer.parseInt
(
read) ;
return
op;
}
@objingid
(
"862453768:43"
)
public
int
choixArticle
(
)
throws
IOException
{
int
mt;
String read =
null
;
boolean
ok =
false
;
do
{
try
{
InputStreamReader lec =
new
InputStreamReader (
System.in) ;
BufferedReader entree =
new
BufferedReader (
lec) ;
read =
entree.readLine
(
) ;
ok =
true
;
}
catch
(
IOException err){
ok =
false
;
}
}
while
(!
ok);
mt=
Integer.parseInt
(
read) ;
return
mt;
}
@objingid
(
"862453768:47"
)
public
void
afficherSoldePanier
(
Client client)
throws
IOException
{
System.out.println
(
"Information concernant le panier client"
);
System.out.println
(
"numClient="
+
client.getNumClient
(
));
System.out.println
(
"solde du panier ="
+
client.getSoldePanier
(
));
}
@objingid
(
"862453768:50"
)
public
void
afficherMouvementPanier
(
Client client)
throws
IOException
{
String s;
System.out.println
(
"Historique des mouvements du panier client:"
+
client.getNumClient
(
));
System.out.println
(
"solde du panier="
+
client.getSoldePanier (
));
for
(
int
i=
0
;i<
client.historiqueMouvement.size
(
);i++
)
{
if
(
client.historiqueMouvement.get
(
i).isMouvementArticle
(
))
{
s=
"+"
; }
else
{
s=
"-"
; }
System.out.println
(
s+
" "
+
client.historiqueMouvement.get
(
i).getPrixArticle
(
));
}
}
@objingid
(
"862453776:42"
)
public
static
void
main
(
String[] argv)
throws
IOException
{
AffichagePanier ap=
new
AffichagePanier
(
);
Client c=
new
Client
(
1234
,"martin"
,"Toto"
,"15 Rue des tests"
);
ap.client.add
(
c);
for
(
int
z=
0
;z<
2
;z++
)
{
ap.afficherSoldePanier
(
ap.client.get
(
z));
for
(
int
j=
0
;j<
2
;j++
)
{
int
op=
ap.choixAction
(
);
if
(
op==
1
)
{
System.out.println
(
"Prix de l'article à ajouter:"
);
int
mt=
ap.choixArticle
(
);
ap.client.get
(
z).ajouterArticle
(
mt);
}
if
(
op==
2
)
{
System.out.println
(
"Prix de l'article à supprimer:"
);
int
mt=
ap.choixArticle
(
);
ap.client.get
(
z).supprimerArticle
(
mt);
}
}
ap.afficherSoldePanier
(
ap.client.get
(
z));
ap.afficherMouvementPanier
(
ap.client.get
(
z));
}
}
}
III. Conclusion▲
Certains détails comme la zone de saisie de code ne sont pas très ergonomiques, mais Objecteering 6 est un outil très performant, où tout se trouve lié, cohérent de la modélisation à l'implémentation. De plus, la génération de code automatique est restée fidèle à notre modélisation et peut faire gagner un certain temps par la suite. Enfin, le fait de déployer ou non certains modules en fonction des besoins reste très intéressant pour le poids de nos différents projets. Nous ne gardons que ce qui nous est utile. Bien entendu, nous n'avons vu que le diagramme de classe en terme de conception, mais tous les diagrammes connus en UML sont réalisables avec cet outil.
IV. Liens utiles▲
V. Remerciements▲
Je remercie Adrien Artero pour sa correction ainsi que Matthieu Brucher et Ricky81 pour leurs critiques constructives.