60
Faculté des Sciences Appliquées UNIVERSITE LIBRE DE BRUXELLES Département IRIDIA - CoDE UNIVERSITE D'EUROPE Mémoire de fin d'études Génération de code à partir d'un diagramme d'états-transitions En vue de l'obtention du Master d'ingénieur civil en Informatique à finalité spécialisée en ingénierie informatique Van Hon BUI Promoteur du mémoire Prof. Hugues BERSINI Année académique 2009-2010

Génération de code à partir d'un diagramme d'états-transitions

Embed Size (px)

Citation preview

Page 1: Génération de code à partir d'un diagramme d'états-transitions

Faculté des Sciences Appliquées UNIVERSITE LIBRE DE BRUXELLESDépartement IRIDIA - CoDE UNIVERSITE D'EUROPE

Mémoire de fin d'études

Génération de code à partir d'un diagramme d'états-transitions

En vue de l'obtention du Master d'ingénieur civil en Informatiqueà finalité spécialisée en ingénierie informatique

Van Hon BUI

Promoteur du mémoireProf. Hugues BERSINI

Année académique 2009-2010

Page 2: Génération de code à partir d'un diagramme d'états-transitions

REMERCIEMENTS

Je souhaite adresser ici tous mes remerciements aux personnes qui m'ont apporté leur aide et qui ont ainsi contribué à la réalisation de ce mémoire.

Je tiens à remercier tout particulièrement mon promoteur de ce mémoire, le Professeur Hugues Bersini, de m'avoir supervisé et encouragé tout au long de ce travail. Sa passion pour la précision m'a permis de développer un esprit plus critique et d'aborder les problèmes avec beaucoup de rigueur.

Je remercie aussi ma famille et mes amis pour leur relecture, leur soutien et leur encouragement tout au long de la réalisation de ce mémoire.

Page 3: Génération de code à partir d'un diagramme d'états-transitions

Table des matièresREMERCIEMENTS............................................................................................................................II1. INTRODUCTION...........................................................................................................................1

1.1. Approches...................................................................................................................11.2. Organisation...............................................................................................................2

2. MODÉLISATION ORIENTÉ-OBJET............................................................................................32.1. Modélisation...............................................................................................................3

2.1.1. Qu'est-ce qu'un modèle ?..................................................................................................32.1.2. Pourquoi modéliser ?.......................................................................................................3

2.2. Orienté-objet...............................................................................................................42.2.1. Approche..........................................................................................................................42.2.2. Concepts importants.........................................................................................................5

2.3. UML, Outil de modélisation graphique.......................................................................72.3.1. Histoire.............................................................................................................................72.3.2. Classification....................................................................................................................82.3.3. Trois modèles...................................................................................................................92.3.4. Avantages.......................................................................................................................10

2.4. Conclusion................................................................................................................103. DIAGRAMME D'ÉTATS-TRANSITIONS..................................................................................11

3.1. Généralités...............................................................................................................113.1.1. Automate et automate à états finis..................................................................................113.1.2. Applications...................................................................................................................12

3.2. État...........................................................................................................................123.2.1. État initial.......................................................................................................................133.2.2. État final.........................................................................................................................13

3.3. Événement...............................................................................................................143.3.1. Événement de type signal...............................................................................................143.3.2. Événement d'appel.........................................................................................................143.3.3. Événement de changement.............................................................................................143.3.4. Événement temporel......................................................................................................15

3.4. Transition..................................................................................................................153.4.1. Condition de garde.........................................................................................................153.4.2. Effet d'une transition......................................................................................................153.4.3. Transition externe...........................................................................................................163.4.4. Transition interne...........................................................................................................16

3.5. Point de choix...........................................................................................................163.5.1. Point de jonction............................................................................................................173.5.2. Point de décision............................................................................................................18

3.6. État composite..........................................................................................................183.6.1. Transition.......................................................................................................................193.6.2. État historique................................................................................................................193.6.3. Points de connexion.......................................................................................................203.6.4. Etat orthogonal...............................................................................................................20

3.7. Conclusion................................................................................................................21

Page 4: Génération de code à partir d'un diagramme d'états-transitions

4. QUALITÉ LOGICIELLE.............................................................................................................224.1. Caractéristiques.......................................................................................................22

4.1.1. Caractéristiques externes................................................................................................224.1.2. Caractéristiques internes................................................................................................23

4.2. Règles de codage....................................................................................................244.3. Design patterns........................................................................................................24

4.3.1. Histoire...........................................................................................................................244.3.2. But général.....................................................................................................................254.3.3. Formalisme.....................................................................................................................254.3.4. Classification..................................................................................................................254.3.5. State pattern ...................................................................................................................264.3.6. Composite pattern..........................................................................................................28

4.4. Conclusion................................................................................................................295. GENERATEUR DE CODE...........................................................................................................31

5.1. Généralités...............................................................................................................315.1.1. Qu'est-ce qu'un générateur de code ?.............................................................................315.1.2. Eléments du générateur de code.....................................................................................31

5.2. Fichier XMI...............................................................................................................335.3. Scanner....................................................................................................................34

5.3.1. DOM..............................................................................................................................355.3.2. Présentation intermédiaire..............................................................................................36

5.4. Analyseur .................................................................................................................365.4.1. Analyseur sémantique....................................................................................................375.4.2. Générateur de code intermédiaire..................................................................................37

5.5. Générateur de code cible.........................................................................................385.5.1. Traduction générale........................................................................................................385.5.2. Etat simple et transition simple......................................................................................405.5.3. Transition complexe.......................................................................................................435.5.4. Etat composite, état initial, état final..............................................................................455.5.5. Etat composite orthogonal.............................................................................................48

5.6. Comparaison............................................................................................................485.7. Utilisation..................................................................................................................485.8. Conclusion................................................................................................................49

6. CONCLUSION GÉNÉRALE.......................................................................................................50BIBLIOGRAPHIE.............................................................................................................................51ANNEXES.........................................................................................................................................52

Page 5: Génération de code à partir d'un diagramme d'états-transitions

1. INTRODUCTIONL'automatisation de la production est toujours l'envie de l'homme: envie d'efficacité, envie de précision, envie de débarrasser ce qui nous encombre,... sans oublier le facteur d'économie. Dans la plupart des domaines comme l'automobile, l'électronique, le textile, l'alimentation... l'automatisation prend une place importante. Paradoxalement, en informatique, le résultat reste très mitigé. Parmi les 14 diagrammes UML utilisés pour la modélisation, il n'y a que 3 diagrammes qui sont utilisés dans la production de code.

La génération automatique de code à partir d'un modèle capturant graphiquement les spécifications du logiciel est de plus en plus couramment utilisée pour implémenter le logiciel ou pour réaliser un prototypage rapide de l'application. Le langage graphique UML, conçu pour modéliser le système d'information, est accepté comme le langage standard de modélisation de l'analyse et la conception de systèmes logiciels.

Les plateformes de modélisation actuelles exploitent des modèles permettant la génération de code, voire des « aller-retours » entre le code et le modèle sans perte d'information. La génération automatique de code à partir des diagrammes graphiques UML est un élément important dans le processus de développements logiciels. A ce jour, alors que les diagrammes de classe et de séquence ont déjà fait l'objet de beaucoup d'attention, il n'en va pas de même pour le diagramme d'états-transitions.

Le diagramme d'états-transitions décrit les changements d'états d'un objet ou d'un composant, en réponse aux interactions. Il représente le cycle de vie d'un objet, de sa naissance à sa disparition, en suivant les différents états par lesquels cet objet transite. Ce diagramme d'état transition est aussi utilisé dans la gestion des dossiers ou des procédures en informant les entités concernées de l'évolution de l'état du dossier ou de la procédure.

Ce mémoire vise concrètement à implémenter un générateur de code automatique qui représentera le plus précisément possible un problème décrit uniquement par son diagramme d'états-transitions.

1.1.ApprochesLe diagramme d'états-transitions est un langage de modélisation graphique où chaque élément de sa notation graphique a une spécification en texte fournissant sa sémantique. En état actuel, il y a deux approches de traduction de ce diagramme en langage de programmation:

1. L'approche par l'utilisation des instructions de condition if-else ou switch-case

2. L'approche par l'utilisation du mécanisme de polymorphiques de l'orienté-objet

1

Page 6: Génération de code à partir d'un diagramme d'états-transitions

Nous avons choisi la deuxième approche afin d'obtenir un résultat, proposé par le générateur, plus compréhensif au fonctionnement cognitive de notre cerveau.

1.2.OrganisationDans un premier temps, nous définissons les notions utiles à la bonne compréhension de la démarche poursuivie au travers d'une introduction à la modélisation orienté-objet et de l'outil de modélisation orienté-objet UML. Toutes les subtilités restent dans la modélisation.

Dans un second temps, nous abordons en profondeur le diagramme d'états-transitions. La compréhension de ce diagramme est primordiale pour procéder adéquatement à la génération de code.

Dans un troisième temps, nous évoquons des caractéristiques du logiciel, des bonnes pratiques du génie logiciel comme les règles de codage, des design patterns permettant la construction des solutions dans une bonne appréciation générale.

Dans un quatrième temps, nous expliquons comment implémenter un générateur de code par l'approche modulation, comment traduire du diagramme d'états-transitions en diagramme de classe et en code Java.

Nous terminons avec une conclusion de cette étude.

2

Page 7: Génération de code à partir d'un diagramme d'états-transitions

2. MODÉLISATION ORIENTÉ-OBJETLa modélisation orienté-objet1 constitue une façon d'appréhender un problème en appliquant des modèles organisés autour de concepts du monde réel. Le concept fondamental est l'objet, qui combine à la fois une structure de données et un comportement. Les modèles orienté-objet permettent de comprendre des problèmes, de modéliser des entités et de concevoir les programmes.

UML, outil graphique commun de la modélisation orienté-objet, normalise les concepts objets. Il permet d’exprimer et d’élaborer des modèles objets, indépendamment de tout langage de programmation, sous différents points de vue et de faciliter la communication entre les parties prenants d'un projet.

2.1.Modélisation

Pour programmer une application, il faut d'abord organiser ses idées, les documenter, puis organiser la réalisation en définissant les modules et étapes de la réalisation. C'est cette démarche antérieure à l'écriture que l'on appelle modélisation.

La modélisation a pour finalité de se concentrer sur les qualités pertinentes pour la solution d'un problème et de faire abstraction des autres [1].

2.1.1. Qu'est-ce qu'un modèle ?

Un modèle est une représentation simplifiée d'une entité (objet, phénomène, processus, système, etc.) du monde réel en vue de le décrire, de le comprendre et de le gérer. Un modèle est un assemblage de concepts avec une connotation pratique: un modèle, c'est une théorie orientée vers l'action qu'elle doit servir [2].

Concrètement, un modèle permet de réduire la complexité d'une entité en éliminant les détails qui n'influencent pas son comportement de manière significative. Il reflète ce que le concepteur croit important pour la compréhension et la prédiction de l'entité modélisée. Les limites de l'entité modélisée dépendent des objectifs du modèle.

2.1.2. Pourquoi modéliser ?

La modélisation d'un système avant sa réalisation permet de mieux comprendre le fonctionnement du système, de maîtriser sa complexité et d'assurer sa cohérence [3].

Un modèle est un langage commun, connu par tous les membres de l'équipe prenante. Il sert de vecteur privilégié pour communiquer. Il est essentiel pour aboutir à une compréhension commune et préciser un problème donné.

1 modélisation orienté-objet ou modélisation orientée objet ou modélisation objet

3

Page 8: Génération de code à partir d'un diagramme d'états-transitions

En ingénierie logicielle, le modèle permet de mieux répartir les tâches et d'automatiser certaines d'entre elles [1]. Il contribue de manière importante à la réduction des coûts et des délais. Par exemple, les plateformes de modélisation savent maintenant exploiter les modèles pour faire de la génération de code.

Le modèle est indispensable pour assurer un bon niveau de qualité et une maintenance efficace. En effet, une fois mise en production, l'application va devoir être maintenue, probablement par une autre équipe [4].

Le choix du modèle influence fortement sur les solutions obtenues. Les systèmes sont modélisés par un ensemble de modèles indépendants. Selon les modèles employés, la démarche de modélisation n'est pas la même [3].

2.2.Orienté-objet

Les techniques de programmation évoluent de la technique de la programmation en langage binaire à celle de la programmation orienté objet. Cette évolution est la conséquence du besoin de concevoir et de maintenir des applications toujours plus complexes [1]. Cette nouvelle technique de programmation a nécessité la conception de nouvelles méthodes de modélisation.

2.2.1. Approche

L’approche orientée objet considère le logiciel comme un ensemble d’objets interagissant via le mécanisme d'envoi de messages. Chaque objet, dispose d’un ensemble d’attributs décrivant son état et d'un ensemble de méthodes caractérisant son comportement.

Un objet définit une représentation abstraite d'une entité atomique du monde réel ou virtuel. Il est caractérisé par trois notions fondamentales :

Les attributs

Il s’agit des données caractérisant l’objet. Ce sont des variables stockant des informations sur l’état de l’objet.

Les méthodes

Les méthodes d’un objet caractérisent son comportement, c’est-à-dire l’ensemble des actions (appelées opérations) que l’objet réalise. Ces opérations permettent de faire réagir l’objet aux sollicitations extérieures. De plus, les opérations sont étroitement liées aux attributs, car leurs actions peuvent dépendre des valeurs des attributs, ou bien les modifier.

L’identité

4

Page 9: Génération de code à partir d'un diagramme d'états-transitions

L’objet possède une identité, qui permet de le distinguer des autres objets. Chaque objet est nommé, ce nom unique de l'objet est son seul et unique identifiant. On construit aussi une identité grâce à un identifiant découlant naturellement du problème (par exemple un produit pourra être repéré par un code, une voiture par un numéro de série, etc.)

L’une des particularités de cette approche est qu’elle incorporent les données et leurs traitements associés au sein d’un unique objet. Cela fait que les objet sont considérés comme des êtres animés qui naissent, vivent et meurent. La modélisation orienté-objet permet de représenter le cycle de vie des objets au travers de leurs interactions [2].

2.2.2. Concepts importants

Nous avons dit que l’approche objet rapproche les données et leurs traitements. Mais cette approche ne fait pas que ça, d’autres concepts importants sont spécifiques à cette approche et participent à la qualité du logiciel :

Notion de classe

Une classe est une entité abstraite qui décrit des caractéristiques (attributs et méthodes) communes à toute une famille d’objets et qui permet de créer des objets possédant ces caractéristiques.

Encapsulation

L’encapsulation consiste à masquer les détails d’implémentation d’un objet, en définissant une interface via un ensemble de méthodes publiques [3]. L’interface est la vue externe d’un objet, elle définit les services accessibles aux utilisateurs de l’objet.

L’encapsulation facilite l’évolution d’une application car on peut modifier l’implémentation d’un objet sans modifier son interface, et donc la façon dont l’objet est utilisé, est conservée.

L’encapsulation garantit l’intégrité des données, car elle permet d’interdire, ou de

5

Fig 2.1: Exemple d'une classe A avec deux attributs attribute1, attribute2 et deux méthodes method1, method2

Page 10: Génération de code à partir d'un diagramme d'états-transitions

restreindre, l’accès direct aux attributs des objets.

Hiérarchies

L’héritage est un mécanisme de transmission des caractéristiques d’une classe vers une sous-classe. Une classe peut être spécialisée en d’autres classes, afin d’y ajouter des caractéristiques spécifiques ou d’en adapter certaines [2]. Plusieurs classes peuvent être généralisées en une classe qui les factorise, afin de regrouper les caractéristiques communes d’un ensemble de classes.

Ainsi, la spécialisation et la généralisation permettent de construire des hiérarchies de classes. L’héritage peut être simple ou multiple. L’héritage évite la duplication et encourage la réutilisation.

Le polymorphisme représente la faculté d’une méthode à pouvoir s’appliquer à des objets de classes différentes. Il augmente la généricité, et donc la qualité du code.

Relations

L'association est une relation de connaissance entre les deux classificateurs (classe ou objet). Simplement, une association indique que deux classes communiquent entre elles. Une association peut avoir n'importe quelle cardinalité à l'une des deux extrémités selon leur relation.

L'agrégation est une relation entre deux classes, spécifiant que les objets d’une classe sont des composants de l’autre classe. Une relation d’agrégation permet donc de définir des objets composés d’autres objets. L’agrégation permet donc d’assembler des objets de base, afin de construire des objets plus complexes [1].

6

Fig 2.2: La classe B hérite de la classe A des attributs et des comportements

Fig 2.3: Exemple de l'association unidirectionnelle

Page 11: Génération de code à partir d'un diagramme d'états-transitions

La composition est une agrégation forte. Lorsque l'objet composé est supprimé, l'objet composite l'est aussi. Par exemple, la relation entre une personne et ses coordonnées est une composition car si on supprime une personne ses coordonnées vont disparaître.

L'orienté objet inscrit la programmation dans une démarche pour affronter la complexité de quelque problème qui soit: une découpe naturelle et intuitive en des parties simples. A fortiori, cette découpe sera d'autant plus intuitive qu'elle s'inspire de notre manière « cognitive » de découper la réalité qui nous entoure. L'héritage, reflet fidèle notre organisation cognitive, en est le témoignage le plus éclatant [5].

2.3.UML, Outil de modélisation graphique

UML (Unified Modeling Language) est un langage visuel dédié à la spécification, la construction, et la documentation des artefact d'un système [6].

UML est non seulement un outil graphique intéressant mais une norme qui s'impose en technologie à objets et à laquelle se sont rangés tous les grands acteurs du domaine. Il fournit les éléments permettant de construire le modèle qui sera le langage du projet. C'est un outil de modélisations graphique commun qui permet de représenter et de communiquer les divers aspects d'un système d'information.

2.3.1. Histoire

Lorsque la programmation par objets prend de l'importance au début des années 1990, la nécessité d'une méthode qui lui soit adaptée devient évidente. Plus de cinquante méthodes apparaissent entre 1990 et 1995 (Booch, Classe-Relation, Fusion, HOOD, OMT, OOA, OOD, OOM, OOSE, etc.) mais aucune ne parvient à s'imposer. En 1994, le consensus2 se fait autour de trois méthodes:

• OMT de James Rumbaugh (General Electric) fournit une représentation graphique 2 Effort de convergence donne le nom Unified Modeling Language. L'adjectif Unified est une marque qu'UML unifié.

7

Fig 2.4: Exemple de la relation d'agrégation

Fig 2.5: Exemple de la relation de composition

Page 12: Génération de code à partir d'un diagramme d'états-transitions

des aspects statique, dynamique et fonctionnel d'un système ; • OOD de Grady Booch (Department of Defense) introduit le concept de paquetage

(package) ; • OOSE d'Ivar Jacobson (Ericsson) fonde l'analyse sur la description des besoins

des utilisateurs (cas d'utilisation, ou use cases).

L'unification a progressé par étapes. Les acteurs les plus importants dans le monde du logiciel s'associent alors à l'effort. L'OMG3 adopte en novembre 1997 UML 1.1 comme langage de modélisation des systèmes d'information à objets. La dernière version d'UML en 2009 est UML 2.2 et les travaux d'amélioration se poursuivent.

2.3.2. Classification

UML 2.2 comporte ainsi 14 types de diagrammes représentant autant de vues distinctes pour représenter des concepts particuliers du système d'information. Ils se répartissent en deux groupes:

Diagrammes structurels (Structure diagrams) sont utilisés pour modéliser la structure statique du système. Ils décrivent les entités dans le système:

• diagramme de classes (Class diagram) • diagramme d'objets (Object diagram) • diagramme de composants (Component diagram) • diagramme de déploiement (Deployment diagram) • diagramme de paquetages (Package diagram) • diagramme de structures composites (Composite structure diagram) • Diagramme de profil (Profile diagram)

Diagrammes comportementaux (Behavior diagrams) focalisent sur le comportement dynamique du système. Ils présentent ce qui se passe dans le système:

• diagramme de cas d'utilisation (Use case diagram) • diagramme d'activités (Activity diagram) • diagramme d'états-transitions (State machine diagram) • diagramme de séquence (Sequence diagram) • diagramme de communication (Communication diagram) • diagramme global d'interaction (Interaction overview diagram) • diagramme de temps (Timing diagram)

Ces diagrammes, d'une utilité variable selon les cas, ne sont pas nécessairement tous produits à l'occasion d'une modélisation. Les plus utiles sont les diagrammes d'activités, de cas d'utilisation, de classes, d'objets, de séquence et d'états-transitions. Est-ce qu'on arrivera à expliquer nos idées avec ces diagrammes ? Il y a encore là un grand écart entre le langage naturel et celui d'informatique. Il y a encore plein de constructions à faire.

3 OMG: Object Management Group

8

Page 13: Génération de code à partir d'un diagramme d'états-transitions

2.3.3. Trois modèles

UML est un outil de modélisation orienté-objet, il nous semble utile4 de le voir sous trois points de vue apparentés et néanmoins distincts:

Modèle de classe

Le modèle de classe, aspects statiques, décrit la structure des objets d'un système: leur identité, leurs relations, leurs attributs et leurs opérations [1].

Le modèle de classes est présenté par les diagrammes de classe. Les classes définissent les attributs et les opérations de chaque objet. La génération permet aux classes de partager une structure et un comportement. Les associations relient les classes.

Modèle d'états

Le modèle d'états, aspects temporels, comportementaux et de contrôle d'un objet, décrit l'ordonnancement des opérations qui le font passer d'un état à un autre: événements marquent les changements, états définissent le contexte d'événements.

Les diagrammes d'états-transitions permettent d'exprimer le modèle d'états. Chaque diagramme représente les séquences d'états et d'événements permises pour une classe donnée. Il fournit une vue globale du comportement d'un seul objet.

Modèle d'interactions

Le modèle d'interactions, aspects interactifs, décrit les interactions entre les objets – la façon dont ils collaborent et qui déterminent le comportement global du système.

Ce sont les diagrammes de cas d'utilisation, les diagrammes de séquence et les diagrammes d'activités qui documentent le modèle d'interactions. Les diagrammes de cas d'utilisation présentent les grands thèmes des interactions entre le système et les acteurs extérieurs. Les diagrammes de séquence décrivent la séquence des interactions entre les objets dans le temps. Et les diagrammes d'activités illustrent le flux de contrôle entre les étapes d'un traitement.

Chaque modèle décrit des aspects différents du système, mais contient des références aux autres modèles. Le tout fournit une vue totale sur le système: la structure, le comportement de l'objet et les collaborations entre les objets. Cinq diagrammes UML cités dans trois modèles sont les plus utilisés.

4 Utile: ce qui nous rend service; profitable avec un regard incomplet

9

Page 14: Génération de code à partir d'un diagramme d'états-transitions

2.3.4. Avantages

UML implique de travailler visuellement et d'exploiter le pouvoir du cerveau humain de saisir rapidement des symboles, des unités et des relations exprimés dans des diagrammes.

Les diagrammes UML nous aident à améliorer notre vue d'ensemble et à éclaircir les relations unissant les éléments du logiciel, tout en nous permettant d'ignorer les détails sans intérêt. UML influe fortement sur la robustesse, la souplesse, et la possibilité de réutiliser des composants logiciels [7].

La génération automatique de code à partir des diagrammes UML est un élément important dans le processus de développements logiciels. Les plateformes de modélisation savent maintenant exploiter les modèles pour faire de la génération de code voire des « aller-retours » entre le code et le modèle sans perte d'information.

2.4.Conclusion

La compréhension de la notion modèle permet de comprendre ses limites. Le modèle représente-t-il la réalité ? Il n’en est qu’une approximation et doit être mise en doute.

L'approche orienté-objet est centrée sur la conception des objets et sur la façon dont les objets collaborent. Il faut assurer que l'objet aie son vrai rôle, sa vraie responsabilité et que ses collaborations avec les autres ne posent pas de problèmes sémantiques.

UML permet de définir un langage commun standardisé pour que ces projets soient menés à bien de façon la plus cohérente possible entre toutes les méthodes qui ont été employées. UML permet de disposer d’un outil graphique qui donne une dimension méthodologique à l’approche objet, ce qui permet de mieux maîtriser sa richesse.

Comme UML n’impose pas de méthode de travail particulière, il peut être intégré à n’importe quel processus de développement logiciel de manière transparente. Il s'agit d'une sorte de boîte à outils permettant l'amélioration progressive des méthodes de travail selon les aspects considérés.

10

Page 15: Génération de code à partir d'un diagramme d'états-transitions

3. DIAGRAMME D'ÉTATS-TRANSITIONSUn système peut être mieux compris si on examine en premier lieu sa structure statique, autrement dit la structure de ses objets et les relations que ces derniers entretiennent à un moment donné (le modèle de classe). Il est ensuite préférable d'étudier les modifications des objets et de leur relations au fil du temps (le modèle d'états).

Le modèle d'états est constitué de plusieurs diagrammes d'états-transitions, un pour chaque classe dotée d'un comportement temporel significatif pour l'application. Le diagramme d'états-transitions est une présentation graphique des automates à états finis qui relie des événements et des états. Les événements représentent les stimuli externes et les états représentent les valeurs des objets.

3.1.Généralités

Le diagramme d'états-transitions est un schéma utilisé en génie logiciel pour décrire le comportement interne d'un objet à l'aide d'un automate à états finis. Il présente les états et les séquences possibles d'actions qu'une instance de classe peut traiter au cours de son cycle de vie en réaction à des événements discrets.

Le diagramme d'états-transitions permet d'avoir une vue globale du comportement de l'élément auquel il est attaché. Mais il ne permet pas de comprendre le fonctionnement global du système puisqu'il ne s'intéresse qu'à un seul élément du système. A la différence d'un diagramme d'interaction qui permet de lier des éléments du système et qui n'offre qu'une vue partielle correspondant à un scénario [8].

Concrètement, un diagramme d'états-transitions est un graphe qui représente un automate à états finis, c'est-à-dire une machine dont le comportement des sorties ne dépend pas seulement de la sollicitation de l'entrée, mais aussi d'un historique des sollicitations passées. La machine change d'état en réponse à des événements extérieurs (sollicitations) donnant lieu à des transitions entre états.

3.1.1. Automate et automate à états finis

Un automate est constitué d'états et de transitions. L'automate passe d'état en état, suivant les transitions, à la réaction de chaque sollicitation de l'entrée. Son comportement des sorties ne dépend pas seulement de la sollicitation de l'entrée, mais aussi d'un historique des sollicitations passées. Cet historique est caractérisé par un état.

Un automate à états finis est un automate qui possède un nombre fini d'états. Il est graphiquement représenté par un graphe comportant des états, matérialisés par des rectangles aux coins arrondis, et des transitions, matérialisées par des arcs orientés liant les états entre eux.

11

Page 16: Génération de code à partir d'un diagramme d'états-transitions

Cette figure montre un exemple simple d'automate à états finis. Cet automate possède deux états (On et Off) et deux transitions correspondant au même événement «push». Cet automate à états finis illustre en fait le fonctionnement d'un interrupteur. Lorsque l'on appuie sur un bouton, la réaction de cet interrupteur dépendra de son état courant (de son historique): il passera à l'état «on», s'il est en état «off», il passera à l'état «off», s'il est en état «on».

3.1.2. Applications

Les diagrammes d'états-transitions s'appliquent de deux manières:

• pour modéliser le comportement d'un objet réactif complexe en réponse à des événements; par exemple, le téléphone, la voiture, la commande, la personne

• pour modéliser des séquences d'opérations légales – spécialisations de protocole ou de langage ou processus; par exemple, le protocole TCP, la navigation, la session.

On peut considérer la deuxième manière d'application comme la spécialisation de la première si l'objet est un langage, un protocole ou un processus.

Utiliser uniquement les diagrammes d'états-transitions pour des objets dépendant de l'état, ayant un comportement complexe [7].

3.2.État

Un état est la condition d'un objet sous laquelle l'objet fournit la même réponse. Cette condition est caractérisée par un jeu de valeurs des attributs d'un objet. Par exemple, l'état d'une banque est solvable ou insolvable, selon que ses actifs excèdent ou non ses dettes.

Un objet peut passer par une série d'états pendant sa durée de vie. Un état représente une période dans la vie d'un objet pendant laquelle ce dernier attend un événement ou accomplit une activité. La configuration de l'état global de l'objet est le jeu des états qui sont actifs à un instant donné [3].

12

Fig 3.1: Diagramme d'états-transitions du bouton boussoire

Page 17: Génération de code à partir d'un diagramme d'états-transitions

Dans le cas d'un diagramme d'états-transitions simple5, il ne peut y avoir qu'un seul état actif à la fois. Dans ce cas, les notions d'état actif et d'état global se rejoignent.

Cependant, la configuration de l'état global peut contenir plusieurs états actifs à un instant donné. Le nombre d'états actifs peut changer pendant la durée de vie d'un objet du fait d'embranchements ou de jointures appelées transitions concurrentes.

Un état se représente graphiquement par un rectangle aux coins arrondis. Certains états, dits composites, peuvent contenir des sous-états ou des régions orthogonaux. Les autres états sont des états simples, ne possèdent pas de sous-structure mais uniquement, le cas échéant, un jeu de transitions internes. Le nom de l'état peut être spécifié dans le rectangle et doit être unique dans un diagramme d'états-transitions.

Un état peut être partitionné en plusieurs compartiments séparés par une ligne horizontale. Le premier compartiment contient le nom de l'état et les autres peuvent recevoir des transitions internes, ou des sous-états, quand il s'agit d'un état composite. Dans le cas d'un état simple, on peut omettre toute barre de séparation.

3.2.1. État initial

L'état initial d'un objet est spécifié avec un rond noir plein

L'état initial, pseudo état, indique l'état de départ par défaut, lorsque le diagramme d'états-transitions, ou l'état enveloppant, est invoqué.

3.2.2. État final

L'état final est spécifié avec un rond noir encerclé.

L'état final, état particulier, indique que le diagramme d'états-transitions, ou l'état 5 sans transition concurrente

13

Fig 3.3: Représentation d'un état initial

Fig 3.4: Représentation d'un état final

Fig 3.2: Représentation d'un état simple

Page 18: Génération de code à partir d'un diagramme d'états-transitions

enveloppant, est terminé. Avec comme contraintes

• qu'il n'a ni de transitions sortantes, ni de régions.

• qu'il n'a ni d'activité « entry », ni d'activité « exit », ni d'activité « do »

3.3.Événement

Un événement est une occurrence ou un fait qui se produit pendant l'exécution d'un système et qui mérite d'être modélisé. Quand un événement est reçu, une transition peut être déclenchée et faire basculer l'objet dans un nouvel état. On peut diviser les événements en plusieurs types explicites et implicites: signal, appel, changement et temporel [1].

3.3.1. Événement de type signal

Un signal est un type destiné explicitement à véhiculer une communication asynchrone à sens unique entre deux objets [9]. L'objet expéditeur crée et initialise explicitement une instance de signal et l'envoie à un objet explicite ou à tout un groupe d'objets.

Un événement de signal est un événement qui consiste à émettre ou à recevoir un signal. Un même objet peut être à la fois expéditeur et destinataire [1]. La syntaxe d'un signal est la suivante :

<nom_événement> '('[ <paramettre> : <type_de_signal> [; <paramettre> : <type_de_signal> ... ] ] ')' 6

Notez la différence entre le signal et événement signal : Un signal est un message entre objets alors que l'événement de signal en est une occurrence dans le temps.

3.3.2. Événement d'appel

Un événement d'appel représente la réception de l'appel d'une opération par un objet. Les paramètres de l'opération sont ceux de l'événement d'appel [3].

Notez que la syntaxe d'un événement d'appel est la même que celle d'un signal. Par contre, les événements d'appel sont des méthodes déclarées au niveau du diagramme de classes.

3.3.3. Événement de changement

Un événement de changement est généré par la satisfaction d'une expression booléenne. L'expression booléenne est testée en permanence. La syntaxe d'un événement de changement est la suivante:

when ( <condition_booléenne> )

6 L'élément entre les crochets peut être omis

14

Page 19: Génération de code à partir d'un diagramme d'états-transitions

3.3.4. Événement temporel

Les événements temporels sont générés par le passage du temps absolu (date précise) ou relatif (une durée). La notation UML d'un temps absolu est le mot-clé when (date = <date>) et celle de relatif, after (<durée>).

Exemple: when (date = 10/10/2010) after (10 secondes)

3.4.Transition

Une transition est le passage instantané d'un état à un autre en occurrence d'un événement. Elle lie un état source et un état cible et indique qu'un objet dans un état source peut exécuter certaines activités et entrer dans l'état cible, si un événement déclencheur se produit et que la condition de garde est vérifiée. La syntaxe d'une transition est la suivante [3]:

[ <événement> ][ '[' <garde> ']' ] [ '/ '<activité> ]

Le même événement peut être le déclencheur de plusieurs transitions. Ces transition doivent avoir des conditions de garde différentes. Si deux transitions sont activées en même temps par un même événement, une seule choisie par hasard se déclenche.

3.4.1. Condition de garde

Une condition de garde, condition de franchissement, est une expression logique sur les attributs de l'objet, ainsi que sur les paramètres de l'événement déclencheur. La condition de garde est évaluée uniquement lorsque l'événement déclencheur se produit. Elle doit être vraie pour que la transition se déclenche et ses effets se produisent [1].

Notez qu'une condition de garde est différente d'un événement de changement: La condition de garde est évaluée une fois que l'événement déclencheur de la transition a lieu. Un événement de changement est réévalué en permanence jusqu'à ce qu'il devienne vrai.

3.4.2. Effet d'une transition

Lorsqu'une transition se déclenche, son effet spécifié par /<activité> dans la syntaxe s'exécute. Il s'agit généralement d'une activité qui peut être une opération primitive comme une instruction d'assignation, un envoi d'un signal, un appel d'une opération, une liste d'activités, etc.

La façon de spécifier l'activité à réaliser est laissée libre (langage naturel ou pseudo-code).

Lorsque l'exécution de l'effet est terminée, l'état cible de la transition devient actif.

15

Page 20: Génération de code à partir d'un diagramme d'états-transitions

3.4.3. Transition externe

Une transition externe est une transition qui modifie l'état actif. Il s'agit du type de transition le plus répandu. Elle est représentée par une flèche allant de l'état source vers l'état cible.

Une transition sans nom d'événement, transition d'achèvement7, est une transition automatique, franchie quand l'activité associée à l'état source est achevée.

3.4.4. Transition interne

Une transition interne est celle qui ne modifie pas l'état courant. Elle permet de prendre en compte un événement sans quitter l’état courant. La syntaxe d'une transition interne reste la même que celle d'une transition classique. Par contre, les transitions internes ne sont pas représentées par des arcs mais sont spécifiées dans un compartiment de leur état associé.

Les transitions internes particulières possèdent des noms d'événement prédéfinis correspondant à des déclencheurs particuliers: entry, exit. Ces mots clefs réservés viennent prendre la place du nom de l'événement dans la syntaxe d'une transition interne.

Une transitions interne entry permet de spécifier une activité qui s'accomplit quand on entre dans l'état. Elle sert souvent à effectuer la configuration nécessaire dans un état.

Une transitions interne exit permet de spécifier une activité qui s'accomplit quand on sort de l'état. Elle sert souvent à procéder à un nettoyage.

3.5.Point de choix

Il est possible de représenter des alternatives pour le franchissement d'une transition. On utilise des pseudo-états particuliers: les points de jonction et les points de décision.

7 Appelée transition d'achèvement par ce qu'elle est provoquée par l'achèvement de l'activité dans l'état source

16

Fig 3.5: Représentation d'une transition externe entre deux états

Fig 3.6: Représentation des transitions internes entry, exit et internalTransition

Page 21: Génération de code à partir d'un diagramme d'états-transitions

3.5.1. Point de jonction

Les points de jonction, représentés par un petit cercle plein, sont un artefact graphique qui permet de partager des segments de transition, l'objectif étant d'aboutir à une notation plus compacte ou plus lisible des chemins alternatifs.

Un point de jonction peut avoir plusieurs segments de transition entrante et plusieurs segments de transition sortante. Par contre, il ne peut avoir d'activité interne ni des transitions sortantes dotées de déclencheurs d'événements [10].

17

Fig 3.7: Représentation d'un diagramme sans point de jonction [4]

Fig 3.8: Représentation d'un diagramme équivalent en utilisant un point de jonction [4]

Page 22: Génération de code à partir d'un diagramme d'états-transitions

Il ne s'agit pas d'un état qui peut être actif au cours d'un laps de temps fini. Lorsqu'un chemin passant par un point de jonction est emprunté toutes les gardes le long de ce chemin doivent s'évaluer à vrai dès le franchissement du premier segment.

3.5.2. Point de décision

Un point de décision, représenté par un losange, possède une entrée et au moins deux sorties. Contrairement à un point de jonction, les gardes situées après le point de décision sont évaluées au moment où il est atteint. Cela permet de baser le choix sur des résultats obtenus en franchissant le segment avant le point de choix. Si le point de décision est atteint et aucun segment en aval n'est franchissable, c'est que le modèle est mal formé [3].

Il est possible d'utiliser une garde particulière, notée [else], sur un des segments en aval d'un point de choix. Ce segment n'est franchissable que si les gardes des autres segments sont toutes fausses. L'utilisation d'une clause [else] est recommandée après un point de décision car elle garantit un modèle bien formé.

3.6.État composite

Un état composite est un état décomposé en régions contenant chacune un ou plusieurs sous-états.

18

Fig 3.9: Représentation d'un point de décision

Fig 3.10: Représentation d'un diagramme avec l'état composite CheckKey

Page 23: Génération de code à partir d'un diagramme d'états-transitions

Implicitement, tout diagramme d'états-transitions est contenu dans un état externe qui n'est usuellement pas représenté. Cela apporte une plus grande homogénéité dans la description: tout diagramme d'états-transitions est implicitement un état composite [4].

L'utilisation d'états composites permet de développer une spécification par raffinements [4]. Il n'est pas nécessaire de représenter les sous-états à chaque utilisation de l'état englobant. Une notation abrégée permet d'indiquer qu'un état est composite et que sa définition est donnée sur un autre diagramme.

3.6.1. Transition

Les transitions peuvent avoir pour cible la frontière d'un état composite et sont équivalentes à une transition ayant pour cible l'état initial de l'état composite [7].

Une transition ayant pour source la frontière d'un état composite est équivalente à une transition qui s'applique à tout sous-état de l'état composite source8. Cette relation est transitive: la transition est franchissable depuis tout état imbriqué, quelle que soit sa profondeur.

Par contre, si la transition ayant pour source la frontière d'un état composite ne porte pas de déclencheur explicite (i.e. s'il s'agit d'une transition d'achèvement), elle est franchissable quand l'état final de l'état composite est atteint.

Les transitions peuvent également toucher des états de différents niveaux d'imbrication en traversant les frontières des états composites.

3.6.2. État historique

Un état historique, également qualifié d'état historique plat, est un pseudo-état qui mémorise le dernier sous-état actif d'un état composite. Graphiquement, il est représenté par un cercle contenant un H.

Il est également possible de définir un état historique profond représenté graphiquement par un cercle contenant un H*. Cet état historique profond permet d'atteindre le dernier état visité dans la région, quel que soit sont niveau d'imbrication, alors que le l'état historique plat limite l'accès aux états de son niveau d'imbrication [3].

8 David Harel a proposé cette interprètation pour simplifier le diagramme d'états-transitions

19

Fig 3.11: Représentation d'une notation abrégée de l'état composite CheckKey

Page 24: Génération de code à partir d'un diagramme d'états-transitions

Une transition ayant pour cible l'état historique est équivalente à une transition qui a pour cible le dernier état visité de l'état englobant. Un état historique peut avoir une transition sortante non étiquetée indiquant l'état à exécuter si la région n'a pas encore été visitée.

3.6.3. Points de connexion

Pour entrer ou sortir d'un état composite sans passer par l'état initial ou l'état final, il faut utiliser les points de connexion et les placer sur la frontière de l'état composite. Les points de connexion permettent la transition traversant la frontière de l'état composite et visant directement d'un état à l'intérieur ou à l'extérieur de cette état composite.

Les points de connexion sont des points d'entrée et de sortie situés sur la frontière d'un état composite. Ils sont respectivement représentés par un cercle vide et un cercle barré d'une croix. Il ne s'agit que de références à un état défini dans l'état composite.

Les points de connexion sont interprétés comme les références à un état cible. Lorsqu'une transition vise le point de connexion, le prolongement de cette transition indique l'état cible. Ils permettent aussi à aboutir à une notation plus compacte ou plus lisible des chemins alternatifs.

3.6.4. Etat orthogonal

Quand un état composite comporte plus d'une région, il est qualifié d'état orthogonal. Lorsqu'un état orthogonal est actif, un sous-état direct de chaque région est simultanément actif, il y a donc concurrence. Un état composite ne comportant qu'une région est qualifié d'état non orthogonal [4].

Graphiquement, dans un état orthogonal, les différentes régions sont séparées par un trait horizontal en pointillé allant du bord gauche au bord droit de l'état composite.

20

Fig 3.12: Représentation d'un état historique plat et d'un état historique profond

Fig 3.13: Représentation des points de connexion

Page 25: Génération de code à partir d'un diagramme d'états-transitions

Chaque région présente un flot d'exécution, peut posséder un état initial et final. Une transition qui atteint la bordure d'un état composite orthogonal est équivalente à une transition qui atteint les états initiaux de toutes ses régions concurrentes.

Toutes les régions concurrentes d'un état composite orthogonal doivent atteindre leur état final pour que l'état composite soit considéré comme terminé [6].

3.7.Conclusion

Un diagramme d'états-transitions est un graphe orienté dont les nœuds sont des états et les arcs des transitions entre états. Il présente aussi les événements qui déclenchent les transitions et le comportement exécuté en réponse aux événements. Il décrit le comportement d'un objet ou le protocole.

Le diagramme d'états-transitions permet de présenter le comportement interne d'un objet en mettant en avant le modèle événementiel. Il est est bien adapté à l'approche orienté-objet en raison des mécanismes d'événements de différents types. Ces mécanismes permettent de faire les autres diagrammes de la norme UML. La grande flexibilité, apportée par la possibilité de définir des transitions internes et par les mécanismes d'hiérarchisation des états, permet de représenter de façon concise et formelle l'ensemble des comportements d'une instance modélisée.

Le diagramme d'états-transitions est le seul de la norme UML à offrir une vision complète et non-ambiguë du comportement de l'élément auquel il est attaché. Il ne permet pas de donner une vue globale du système, il est surtout adapté à la phase de réalisation du logiciel. Il est uniquement utilisé pour les objets dépendant de l'état et ayant un comportement complexe.

21

Fig 3.14: Exemple d'utilisation d'un état orthogonal pour modéliser le fait que la réussite d'un cours implique trois travaux en parallèle [6].

Page 26: Génération de code à partir d'un diagramme d'états-transitions

4. QUALITÉ LOGICIELLEL'approche orientée objet tend à éclater les applications en composants plus simples et réutilisables. Cependant, cette approche peut vite devenir un piège lorsque le découpage s'effectue sans règles précises. L'application finit par être saturée par la complexité du codage.

Le génie logiciel comporte des aspects de gestion de projet afin de produire un logiciel de qualité. Le terme génie logiciel désigne l'ensemble des mesures, des méthodes, des techniques et outils concourant à la production d'un logiciel.

La qualité d'un logiciel dépend entièrement de sa construction, elle est devenu un sujet central en génie logiciel. Elle est une appréciation globale d'un logiciel, basée sur de nombreuses caractéristiques. Les règles de codage et les patterns de design sont des bonnes pratiques pour améliorer la qualité logicielle.

4.1.Caractéristiques

Une appréciation globale de la qualité tient autant compte des caractéristiques externes, directement observables par l'utilisateur, que des caractéristiques internes, observables par les ingénieurs lors des revues de code ou des travaux de maintenance [11]. Un bon programme doit posséder toutes ces deux caractéristiques.

4.1.1. Caractéristiques externes

Les caractéristiques externes sont des caractéristiques que l'utilisateur du logiciel intéresse, y compris les suivantes :

Validité: aptitude d'un produit logiciel à remplir exactement ses fonctions, définies par le cahier des charges et les spécifications.

Facilité d'emploi: facilité d'apprentissage, d'utilisation, de préparation des données, d'interprétation des erreurs et de rattrapage en cas d'erreur d'utilisation.

Efficacité: utilisation optimales des ressources matérielles, y compris la mémoire et de temps d'exécution.

Fiabilité ou robustesse: aptitude d'un produit logiciel à fonctionner dans des conditions anormales, avoir une longue période moyenne entre les pannes.

Intégrité: aptitude d'un logiciel à protéger son code et ses données contre des accès non autorisés ainsi qu'à veiller à ce que l'accès aux données correctement fait.

Capacité d'adaptation: Le système peut être utilisé, sans modification, dans des

22

Page 27: Génération de code à partir d'un diagramme d'états-transitions

applications ou des environnements autres que celles pour lesquelles il a été spécialement conçu.

Robustesse: La mesure dans laquelle un système continue de fonctionner en présence d'entrées invalides ou des conditions environnementales stressantes.

Certaines de ces caractéristiques se chevauchent, mais tous ont des nuances différentes qui s'appliquent plus dans certains cas, moins dans d'autres.

4.1.2. Caractéristiques internes

Les caractéristiques internes sont des caractéristiques que le développeur du logiciel intéresse [11]. Ce mémoire est centré sur le code et met l'accent sur les caractéristiques internes suivantes:

Extensibilité (maintenance): facilité avec laquelle un logiciel se prête à une modification ou à une extension des fonctions qui lui sont demandées.

Flexibilité: facilité avec laquelle un logiciel se prête à une modification pour s'adapter à des utilisations ou à des milieux autres que celles pour lesquelles il a été spécialement conçu.

Portabilité: facilité avec laquelle un logiciel peut être transféré sous différents environnements matériels et logiciels.

Réutilisabilité: facilité avec laquelle un logiciel peut être réutilisé, en tout ou en partie, dans de nouvelles applications.

Compatibilité: facilité avec laquelle un logiciel peut être combiné avec d'autres logiciels.

Simplicité: facilité avec laquelle on peut lire et comprendre le code source d'un système.

Vérifiabilité: facilité de préparation des procédures de test pour vérifier si le système répond à ses exigences.

En génie logiciel, divers travaux ont mené à la définition de la qualité du logiciel en termes de facteurs, qui dépendent, entre autres, du domaine de l'application et des outils utilisés.

Le nombre de caractéristiques du logiciel est important. Doit-on prendre en compte toutes ses caractéristiques lors de la construction d'un logiciel ? Pourront-elles totalement rencontrées dans l'avenir ? Ces questions persistent tant que des critères d'évaluation ne sont pas complets et ils ne font pas l'objet de standard commun.

23

Page 28: Génération de code à partir d'un diagramme d'états-transitions

4.2.Règles de codage

Le développement de logiciels pose de grands problèmes de coordination, en raison de la quantité importante d'informations à communiquer entre les intervenants. Pour ces raisons, le développement de logiciels dans un contexte professionnel doit suivre des règles strictes permettant le travail en groupe et la maintenance du code.

Les règles de codage sont un ensemble de règles à suivre pour uniformiser les pratiques de développement logiciel, diffuser les bonnes pratiques de développement et éviter les erreurs de développement classiques au sein d'un groupe de développeurs [12].

Les règles de codage s'articulent autour de plusieurs thèmes, les plus courants étant:

• Le nommage et l'organisation des fichiers du code source

• L'indentation ou le style d'indentation, l'espace blanc

• Les conventions de nommage, ou règles de nommage

• Les commentaires et documentation du code source

• Recommandations sur la déclarations des variables

• Recommandations sur l'écriture des instructions, des structures de contrôle et l'usage des parenthèses dans les expressions.

Les règles de codage permettent d'assurer une meilleure lisibilité du code en utilisant le même style de codage et en évitant les constructions qui rendent le code difficile à lire. Elles participent à la bonne communication et donc à la qualité logicielle. Plus l'importance des développement est élevée, plus les besoins en règles de codages sont élevés.

4.3.Design patterns

Les Design Patterns, patrons de conception, sont un recueil de bonnes pratiques (solutions) de conception pour un certain nombre de problèmes récurrents en programmation orientée objet. Ces solutions sont testées et approuvées leur utilité. Avec le temps, elles sont devenues des solutions standard pour répondre à des problèmes d'architecture et de conception des logiciels. Les Design Patterns sont un concept de génie logiciel, le plus souvent indépendants du langage de programmation.

4.3.1. Histoire

Les Design Patterns sont à origine du travail de l'architecte Christopher Alexander publié en 1977 et sont connu dans le monde informatique après la publication du ouvrage « Design Patterns: Elements of Reusable Object-Oriented Software » en 1994

24

Page 29: Génération de code à partir d'un diagramme d'états-transitions

4.3.2. But général

« Chaque patron décrit un problème qui se manifeste constamment dans notre environnement, et donc décrit le cœur de la solution à ce problème, d'une façon telle que l'on puisse réutiliser cette solution des millions de fois, sans jamais le faire deux fois de la même manière » - Christopher Alexander, 1977

Les Design Patterns représentent des savoir-faire des concepteurs de logiciels relatives à la description de problèmes récurrents de conception et de leurs solutions. En programmation orienté-objet, ces solutions sont destinées à régler les problèmes de relations ou d'interactions en les différentes classes ou modules avec pour but de minimiser les interactions entre les différentes classes ou modules, réduire la complexité, rendre les codes plus lisibles, augmenter la qualité du programme et diminuer le temps nécessaire au développement d'un logiciel. Ils définissent aussi un vocabulaire commun entre les différents acteurs de l'écriture d'un logiciel [7].

4.3.3. Formalisme

Il n'y a pas de formalisme standardisé pour décrire les patterns. Pour tant il y a des formalismes qui sont plus connus que les autres. Voici les principaux éléments:

• Nom: augmente le vocabulaire, réifie une idée de solution, permet de mieux communiquer.

• Structure: présentation graphique du pattern (diagramme de classe)

• Collaborations: les interactions entre les participants

• Problème: quand appliquer la forme, le contexte...

• Solution: les éléments de la solution, leurs relations, responsabilités, collaborations. Pas de manière précise, mais suggestives...

• Conséquences: résultats et compromis issus de l'application de la forme

4.3.4. Classification

Les patterns de conception sont classifiés selon trois grandes familles :

• Patterns de création: ils permettent de résoudre les problèmes liés à la création et la configuration de classes et d'objets.

• Patterns de structuration: ils permettent de résoudre les problèmes liés à la structuration des classes et leur interface en particulier.

• Patterns de comportement: ils permettent de résoudre les problèmes liés aux comportements, à l'interaction entre les classes, les objets.

25

Page 30: Génération de code à partir d'un diagramme d'états-transitions

Voici les différents patterns les plus connus:

Création Structuration ComportementClass Factory Method Adapter(class) Interpreter

Template MethodObject Abstract Factory

BuilderPrototypeSingleton

Adapter(objet)BridgeCompositeDecoratorFacadeFlyweightProxy

Chain of ResponsCommandIteratorMediatorMementoObserverStateStrategyVisitor

Un pattern est accompagné des conseils portant sur la pertinence de son usage et des arguments d'arbitrage sur la façon de l'utilisateur. En particulier, un pattern a été soigneusement choisi par d'autres personnes et appliqué à des problèmes rencontrés dans le passé. En conséquence, il n'est plus susceptible d'être correct à une solution spécifique non-testée. On l'étudie quand on l'utilise [1].

4.3.5. State pattern

Le state pattern, bien utilisé, permet de concevoir des applications de qualité: facile à comprendre, à débugger, à maintenir, à faire évoluer [13].

Le state pattern, patron de conception état, est un design pattern comportemental utilisé pour représenter les différents états d'un objet. « Il permet à un objet de modifier son comportement quand son état interne change. Tout se passe comme si l'objet changeait de classe » [14].

Problème:

Le comportement d'un objet dépend de son état, le changement de comportement doit intervenir dynamiquement en fonction de cet état.

Le comportement d'un objet a les conditions logiques de type if-else or switch-case. Le code de l'objet devient trop complexe.

Solution:

Créer une classe abstraite State qui déclare l'interface encapsulant le comportement associé à un des états possibles pour de l'objet Context.

26

Page 31: Génération de code à partir d'un diagramme d'états-transitions

Pour chaque état possible de l'objet Context, créer une classe les sous-classes ConcreteState qui dérive de la classe State et qui implémente les comportements associés à cet état.

Ajouter la classe Context définit l'interface d'intérêt pour les clients et mémorise l'état courant (une instance d'une des sous-classes ConcreteState).

Structure:

Collaborations:

La classe Context est la classe qui peut avoir plusieurs états internes. Quand l'instance Context reçoit un message, elle délègue ce message à l'état concret courant et change peut-être son état.

Soit l'instance de la classe Context, soit l'état courant (instance de la sous-classe State) décide de l'état qui succède.

Conséquences:

Encapsuler dans la hiérarchie issue de State les différences de comportements dépendant de l'état et les partitionne selon les états.

Rendre explicite les transitions entre états, par passage entre les différentes sous-classes possibles de State.

Éviter d'avoir de grosses portions de code constituées uniquement de switch-case ou de if-else.

Ajouter des états supplémentaires se fait beaucoup plus simplement en utilisant des objets à états.

27

Fig 4.1: Diagramme de classe du State Pattern

Page 32: Génération de code à partir d'un diagramme d'états-transitions

Rendre le coupage de code plus naturel et faciliter la compréhension et la réutilisation du code.

Multiplier le nombre de classe fait augmenter le nombre de déclarations et d'implémentations.

4.3.6. Composite pattern

Le composite pattern est un patron de conception (design pattern) structurel. Il est utilisé pour représenter une structure hiérarchique. Il permet au client de traiter de la même et unique façon les objets individuels et les combinaisons de ceux-ci.

Structure:

Problème:

Quand vous devez représenter une hiérarchie d'objets et ignorer la différence entre un composant simple et un composant en contenant d'autres (interface uniforme).

Solution:

Créer la classe Component déclarant l'interface pour la composition d'objet, une interface pour l'accès aux composants enfants et mettant en œuvre le comportement par défaut. L'objet Client utilise ces interfaces pour interagir avec les objets dans la composition.

28

Fig 4.2: Diagramme de classe du Composite Pattern

Page 33: Génération de code à partir d'un diagramme d'états-transitions

Créer la sous-classe Leaf dérivant de la classe Component, cette sous-classe définit les comportements de l'objet de la composition.

Créer la sous-classe Composite, dérivant de la classe Component, définit le comportement pour les composants ayant des enfants, stocke les composants enfants et met en œuvre la gestion des enfants de l'interface Component.

Collaborations:

Le client utilise l'interface de la classe Composant pour manipuler les objets de la structure composite.

Si l'objet manipulé est une feuille (Leaf), la requête est directement traitée.

Si l'objet manipulé est un Composite, il transfère la requête à ses composants.

Conséquences:

Le code du client est très simplifié puisqu'il n'a accès qu'à une seule interface pour tous les nœuds.

Dans une architecture réelle, de nombreuses classes dérivent de Composite et de Leaf. Tout le code concernant l'organisation en arbre est réutilisé. L'ajout d'un nouveau type de nœud est simple.

Un problème peut se poser si on souhaite restreindre à certains types de nœuds fils pour un certain composite.

4.4.Conclusion

Les caractéristiques du logiciel sont des appels de qualité que l'équipe de développement du logiciel doivent prendre en considération. Elles sont là pour nous inviter à la réussite du projet logiciel en tenant compte non seulement des développeurs mais aussi des utilisateurs.

L'application de règles de codage permet de fournir un code plus lisible, plus facile à la maintenance. Elle joue un rôle important dans la qualité et les performances du logiciel en améliorant la compréhension du code, la participation à la construction du projet. Le non-respect des règles de codage crée des projets qui deviennent ingérables, entraine l'échec du projet logiciel.

Design patterns, bonnes pratiques, existent comme des solutions avérées et approuvées aux problèmes de conception. Ils codifient, sous forme de « problèmes-solutions », des principes de conception. Ils apportent des solutions élégantes aux concepteurs de projet. Ils sont aussi utiles pour définir un vocabulaire commun entre les différents acteurs de l'écriture d'un logiciel.

29

Page 34: Génération de code à partir d'un diagramme d'états-transitions

Le state pattern permet d'implémenter, de manière élégante, des objets selon le modèle d'états. Il rend le coupage de code plus naturel et facilite la compréhension et la réutilisation du code. Le composite pattern permet d'organiser une structure hiérarchique de manière plus simple.

Le state pattern multiplie le nombre de classe et fait augmenter le nombre de déclarations et d'implémentations

30

Page 35: Génération de code à partir d'un diagramme d'états-transitions

5. GENERATEUR DE CODELa partie qui suit est consacré à l'implémentation du générateur de code utilisant les informations de modélisation d'un diagramme d'états-transitions et générant le code Java.

L'implémentation est faite par modules. Chaque module correspond à chaque élément du générateur de code. Il a sa précise fonctionnalité et ses responsabilités.

5.1.Généralités

La génération automatique d’applications reste-t-elle vraiment une technologie sophistiquée même si dans l’esprit, elle a pour but principal de simplifier les développements ? Cette généralité permet d'approcher l'outil efficace de développement de logiciels.

5.1.1. Qu'est-ce qu'un générateur de code ?

Le générateur de code est un programme, outil qui utilise les données (langage source) et le contexte d'utilisation pour générer le langage cible. A la différence du compilateur qui fait référence à la traduction exacte d'un langage source vers un langage cible, le générateur de code est plutôt un interprète, qui ne traduit pas fidèlement un langage vers un autre.

Un générateur de code est peut être un simple script de correspondance de clefs, un programme de création d'API9, jusqu'à un outil complet de traduction de modèles de logique métier en applications complètes et fonctionnelles. Les types de générateurs sont multiples, selon les besoins, mais la fonctionnalité principale reste la même : produire du code automatique et soulager le développeur de certaines lourdeurs de mise en place. Le code obtenu peut être aussi bien du code source prêt à être modifié ou compilé, du byte-code pour une machine virtuelle, un exécutable.

Ce générateur de code est en mesure de produire le code adéquat, de manière cohérente, rapide sûre en utilisant les données du diagramme d'états-transitions combinées avec les règles fondamentales du développement du logiciel (règles de bonnes pratiques en génie logicielle). Il apporte une aide réelle aux projets de développement, en s'occupant du travail laborieux. Il apporte des gains économiques réels mais aussi un changement des méthodes et processus de développement. Il contribue considérablement à la qualité de code à sa cohérence et sa facilité à réutiliser.

5.1.2. Eléments du générateur de code

Techniquement, le générateur de code a presque tous les éléments d'un compilateur10:

9 API: Application Programming Interface10 Eléments d'un compilateur: Scanner, analyseur, Générateur

31

Page 36: Génération de code à partir d'un diagramme d'états-transitions

l'analyseur lexical, l'analyseur syntaxique et l'optimiseur sont quasi inexistants, l'analyseur sémantique peut être présent pour valider le modèle.

Le code source ou langage source est un texte dans un fichier, il présente un ensemble de données bien structurées.

Le scanner est un simple lecteur qui lit le code source pour produire une structure de données souvent plus proche du code source.

L'analyseur utilise les données fournies par le scanner et les réorganise en une structure intermédiaire plus logique et facile à utiliser. Il permet de simplifier le générateur de code cible, de rendre le générateur de code cible indépendant du langage source.

Le générateur, appelé aussi générateur de code cible, avale les données fournies par l'analyseur et produit le code cible.

Le code cible ou langage cible est le langage de programmation. Dans le cadre du présent travail nous avons choisi le langage Java.

Cette organisation en modules permet la maîtrise du fonctionnement du programme, la facilité d'évolution du programme et du code cible ainsi que le respect du processus de construction d'un compilateur [15].

32

Fig 5.1: Eléments du générateur de code, le rectangle présente les données et le rectangle à coins arrondis présente l'unité de traitement

Page 37: Génération de code à partir d'un diagramme d'états-transitions

5.2.Fichier XMI

XMI, XML Metadata Interchange, est un standard créé par l'OMG11 pour l'échange d'informations de métadonnées UML basé sur XML [16]. Il permet de décrire des objets sous forme XML. Il est utilisé comme standard d'échange entre différents outils de modélisation.

L'usage principal de XMI consiste à favoriser l'échange de modèles UML entre les ateliers de développement de programmes de différents fournisseurs. Afin d'échanger des modèles d'un outil à un autre, les modèles UML sont exportés et importés au format XMI. Le XMI est en fait une représentation textuelle du modèle UML.

A partir de modèle graphique du diagramme d'états-transitions, nous pouvons obtenir le fichier XMI via les outils de modélisation. Il existe beaucoup de logiciels sur le marché qui convertissent les diagrammes UML en fichier XMI. (Enterprise Architect, Rational Rose, Together, UModel, Visual Paradigm, Eclipse UML, ArgoUML, ...). Nous avons choisi le logiciel UModel, développé par Altova, pour des raisons de respect de standard UML et de simplicité de l'interface graphique.

XMI est un document XML bien-formé et validé. Ce document contient des éléments qui se nichent les uns dans les autres et ne se chevauchent pas. Un élément peut posséder un certain nombre d'attributs qui définissent des caractéristiques spécifiques à cet élément. Les valeurs correspondant à ces attributs sont toujours mises entre guillemets.

Nous allons détailler chaque élément du fichier XMI du diagramme d'états-transitions. A notez que chaque élément possède généralement des attributs typiques suivants:

• L'attribut « type » définit le type de l'élément.

• L'attribut « id » spécifie l'identité unique de l'élément, la valeur de cet attribut est unique, sert de valeur de référence.

• L'attribut « name » indique le nom accordé à l'élément en question.

L'élément « PackagedElement » englobe tous les éléments définissant l'information du diagramme d'états-transitions. Il possède un élément enfant direct « region ».

L'élément « region » contient directement des éléments « subvertex » et des éléments « transtion ». Les valeurs des attributs de cet élément ne sont pas exploitées dans ce projet.

L'élément « subvertex » qui contient directement des éléments « region » et des éléments « connectionPoint », définit l'état composites. L'élément « subvertex » qui ne contient pas les éléments enfants, définit l'état simple ou le pseudo état.

11 MOG: Object Management Group

33

Page 38: Génération de code à partir d'un diagramme d'états-transitions

• L'attribut « name »,nom de l'état, est utilisé pour le nom de classe dans notre projet.

• L'attribut « type » a pour valeur State, FinalState ou Pseudostate

• L'attribut « kind » existe quand l'attribut « type » a pour valeur Pseudostate. Il peut avoir une des valeurs comme: initial, deepHistory, shallowHistory, join, fork, junction, choice.

L'élément « subvertex » est l'élément principal du diagramme d'états-transitions. Il demande une attention plus particulière lors de la réalisation du projet.

L'élément « transition » définit l'information d'une transition. Il a l'attribut « source » (référencé à la source de transition) et l'attribut « target » (référencé au cible de transition). Il peut contenir des éléments « trigger », « guard », « effect ».

L'élément « trigger » définit le nom de transition.

L'élément « guard » spécifie la condition de transition.

L'élément « effect » spécifie l'activité qui se produit si la transition aura lieu.

Il est capital d'exploiter toutes les informations du fichier XMI pour la génération de code.

5.3.Scanner

Nous connaissons l'information contenue dans chaque élément du fichier XMI. Il convient de choisir l'outil parseur pour extraire les données de ce fichier. Avec les données extraites, le scanner produit une présentation intermédiaire sous forme d'hiérarchie d'objets.

Les parseurs XML sont également divisés selon l'approche qu'ils utilisent pour traiter le document. On distingue actuellement deux types d'approches :

• L'approche hiérarchique permettent de construire une structure hiérarchique contenant des objets représentant les éléments du document. La principale API utilisant cette approche est DOM12.

• L'approche événementielle permettent de réagir à des événements et de renvoyer le résultat à l'application utilisant cette API. SAX est la principale interface utilisant l'aspect événementiel.

Nous avons besoin d'un parseur qui nous permet de créer une structure hiérarchique contenant les données contenues dans le document XMI. Le niveau d'implication des éléments dans le fichier XMI fournit l'information sur la structure du diagramme. Le DOM est choisi.12 DOM: Document Object Model

34

Page 39: Génération de code à partir d'un diagramme d'états-transitions

5.3.1. DOM

Le DOM est une recommandation du W3C qui définit une interface de programme d'application (API) indépendant de tout langage de programmation et de toute plate-forme. Il est construit pour accéder ou modifier au contenu des documents XML. Le DOM permet de construire une arborescence de la structure d'un document et de ses éléments. Il est implanté sous forme de classes hiérarchisées. Voici le modèle simplifié du DOM:

35

Fig 5.2: Diagramme de classe de DOM donnant une image globale de l'interface DOM

Page 40: Génération de code à partir d'un diagramme d'états-transitions

Ce modèle nous fournit une interface pour accéder au contenu du document XML, est très utile à l'écriture du programme. Ce diagramme donne une image globale de l'interface DOM, une idée sur les possibilités d'accès à chaque nœud.

Java comme la plupart des langages implémente le modèle DOM [17]. La construction d'une nouvelle instance de parseur DOM permet la lecture du fichier XMI. Cet instance DOM crée une structure hiérarchique avec tout les nœuds du fichier qu'elle analyse. Cette structure hiérarchique reflète exactement la structure de tags dans le fichier XMI.

5.3.2. Présentation intermédiaire

Grâce à l'interface DOM, nous parcourons nœud par nœud. Pour chaque nœud, le scanner crée un objet avec les données extraites du nœud en question et le met en relation avec les autres objets, selon la relation de ce nœud, pour créer une structure hiérarchique d'objets qui est une présentation fidèle de celle des nœuds du DOM. Voici le diagramme de classes présentant la structure d'objets en question :

Dans le code de notre projet, le parquet de code xml assume toutes les tâches du scanner. Le résultat fourni par le scanner sera utilisé par la suite par l'analyseur.

5.4.Analyseur

L'analyseur a deux rôles principaux, l'analyseur sémantique et le générateur de code intermédiaire.

36

Fig 5.3: Diagramme de classe présentant la structure de données du fichier XMI

Page 41: Génération de code à partir d'un diagramme d'états-transitions

5.4.1. Analyseur sémantique

Cet analyseur sémantique a pour objectif de valider le modèle car ce n'est pas rare qu'on ne dessine pas correctement un diagramme. Si celui-ci est incorrect, le générateur de code a des difficultés pour fournir un code correct. Il y a des règles grammaticales qu'on peut trouver via les spécifications du diagramme d'états-transitions [6].

Quelques exemples de ces règles:

• L'état initial doit avoir au minimum une transition sortante.

• L'état final ne doit pas avoir une transition sortante.

• Le point d'entrée ou de sortie doivent avoir au minimum une transition entrante et une transition sortante.

• Chaque état doit avoir au minimum une transition entrante ou sortante.

Un ensemble de règles de syntaxe forme une grammaire, l'analyseur sémantique implémente cette grammaire.

5.4.2. Générateur de code intermédiaire

A partir des données fournies par le scanner, le générateur de code intermédiaire produit une structure de données plus facile à utiliser par le générateur de code cible. Il est là pour rendre le générateur de code cible indépendant du fichier XMI et aussi pour simplifier le générateur de code cible. Voici le résultat obtenu par l'analyseur:

37

Fig 5.4: Diagramme de classe présantant le code intermédiaire

Page 42: Génération de code à partir d'un diagramme d'états-transitions

Ce générateur de code intermédiaire est écrit en fonction de la logique d'usage de données par le générateur de code cible et aussi en fonction de la structure de données fournies par le scanner. En principe, on pousse toutes les opérations de recherche de données du générateur de code cible vers l'analyseur. Idéalement l'analyseur fournit toutes les données nécessaires à usage direct au générateur de code cible. Cela facilite la génération de nouveau code de meilleure qualité.

Cet analyseur réorganise les transitions en fonction de subvertex (état ou pseudostate) car le générateur de code cible doit connaître les transitions de chaque subvertex. La relation d'agrégation de la classe Subvertex à la classe Transition représente cette réorganisation.

Pour chaque transition, le générateur de code cible doit connaître le subvertex cible de cette transition mais pas id cible de la transition. L'association de classe Transition à la classe Subvertex est ajoutée.

Les opérations d'analyse des conditions de transition doivent aussi être faites pour essayer de fournir une interprétation sur les conditions. A l'état actuel, l'analyseur est capable d'interpréter des conditions mathématiques.

5.5.Générateur de code cible

Dans cette partie, nous utilisons toutes les informations théoriques pour écrire le générateur de code cible: Le diagramme d'états-transitions est la base de notre générateur de code cible. La compréhension de ce diagramme est primordiale pour avoir une interprétation correcte. La maîtrise de l'orienté objet est requise pour fournir, à partir du diagramme d'états-transitions, le modèle de classes sémantiquement correct. Les caractéristiques de qualité sont là comme le surveillant discret de notre projet. Les patterns participant dans notre proposition sont les éléments qui permettent un code simple de qualité. Le respect des règles de codage est très indispensable pour la lisibilité du code cible.

5.5.1. Traduction générale

L'utilisation du state pattern et du composite pattern nous conduit à aboutir à la traduction du diagramme d'états-transitions en diagramme de classe présentant la structure du code cible suivant:

38

Page 43: Génération de code à partir d'un diagramme d'états-transitions

Nous reconnaissons le state pattern via l'ensemble de classes Client, Context, AbstractState, ConcreteState et le pattern composite via la composition de classes AbstractState, ConcreteState, AbstractCompositeState, ConcreteCompositeState.

Le nombre de classes ConcreteState correspond au nombre d'état simple dans le diagramme d'états-transitions. Le nombre de classes ConcreteCompositeState égale celui d'états composites.

L'élément interface est ajouté pour forcer la classe Context et la classe AbstractState à déclarer toutes les méthodes correspondant aux noms des événements (trigger) et pour créer des instances d'état de la classe Context.

Le générateur de code cible est organisé selon le modèle d'états. Il possède 7 états qui correspondent à 7 types de classe de la structure générale du code cible. A chaque état, le générateur de code cible génère un type de classe.

Ce qui est intéressant, dans cette démarche, est d'utiliser directement le code cible dans le générateur pour simplifier le générateur mais aussi pour tester et vérifier la limite du code cible.

39

Fig 5.5: Diagramme de classe présentant une structure générale du code cible

Fig 5.6: Diagramme d'états-transitions présentant 7 états du générateur de code cible

Page 44: Génération de code à partir d'un diagramme d'états-transitions

Le fait d'utiliser directement le code dans le générateur de code crée une boucle sans fin que j'ai appelé la boucle d'évolution. La modification de la structure de base du générateur de code cible implique une limite du code cible, la mise en question du code cible est requise. Inversement, la modification du code cible signifie une évolution du code cible. Le fait d'intégrer ce nouveau code cible dans le générateur de code permet de le tester et de le vérifier de nouveau.

Nous allons détailler comment traiter chaque élément du diagramme d'états-transitions via des exemples. D'abord, nous introduisons une courte explication de l'exemple utilisé. Puis nous présentons le diagramme d'états transitions modélisant le comportement de l'objet en question. En suite, nous fournissons les règles de traduction et la logique du programme de générateur de code cible. Et après, nous montrons la proposition du diagramme de classes correspondant en détaillant les nouveautés et en minimisant ce qui sont déjà traité pour ne pas encombrer le sujet en question. En fin, nous listons un extrait de code pour illustré le résultat concret.

5.5.2. Etat simple et transition simple

Chaque objet d'une classe a ses propres valeurs, ses propres liens mais aussi ses propres états. Pour illustrer le traitement accordé à l'état simple et à la transition simple 13, nous allons utiliser la classe TrafficLight14 (feu de signalisation) avec trois états présenté par trois couleurs et trois méthodes toRed, toGreen, toYellow pour changer ses couleurs en sachant qu'à chaque moment, il y a une seule transition possible.

Le diagramme d'états-transitions:

Les règles de traduction:

− Chaque état correspond à une classe, soit classe concrète simple (état simple), soit classe concrète composite (état composite).

− Chaque transition qui commence à partir d'un état et qui a un événement (trigger),

13 Transition simple est une transition sans condition de garde, ni l'effet14 TrafficLight est un exemple utilisé dans l'article Object-Oriented Refactoring of Existing Immune Models, Hugues

Bersini, 2009

40

Fig 5.7: Diagramme d'états-transitions du feu de signalisation (TrafficLight)

Page 45: Génération de code à partir d'un diagramme d'états-transitions

est traduite en méthode de la classe source de cette transition. Le nom de cette méthode a le nom de l'événement. Cette méthode retourne une instance de la classe cible.

− La transition interne ne possède pas l'état cible, la méthode d'origine de cette transition retourne une valeur « null ».

− Nous ajoutons la classe abstraite State (AbstractTrafficLightState) déclarerant l'interface encapsulant le comportement des états, la classe abstraite composite (CompositeTrafficLightState) définissant le comportement des composants (gestion des ses composants) ayant des états et la classe Context (TrafficLight) déléguant la requête aux états courants.

− La classe concrète composite State (TrafficLightState) est la classe d'état de la classe Context, elle gère tous les états de la classe Context.

− La classe Client (TrafficLightClient) sert à tester le code généré.

Le diagramme de classes:

Quand l'instance de la classe Context TrafficLight reçoit une requête, elle délègue cette requête à son état. A son tour, cet état, instance de la classe TrafficLightState, délègue cette requête à son composant actif qui fait partie d'un des trois instances de classe

41

Fig 5.8: Diagramme de classe du feu siginalisation

Page 46: Génération de code à partir d'un diagramme d'états-transitions

concrète simple RedTrafficLightState, GreenTrafficLightState, YellowTrafficLightState. Le diagramme de classe est proche de celui proposé par Hugues BERSINI [18].

L'extrait de code:

Le code complet de ce premier exemple est listé en annexes pour que le lecteur puisse apprécier le résultat concret du générateur car c'est trop difficile d'évaluer un générateur sans regarder le respect de convention, des règles de codage.

Un extrait du code de la classe RedTrafficLightState, classe concrète simple provenant de l'état Red, implémente le comportement concret du méthode toGreen:

package logic;

public class RedTrafficLightState extends AbstractTrafficLightState {public RedTrafficLightState() {

super();}

public AbstractTrafficLightState toGreen() {// TO DOreturn TrafficLightInterface.GREENTRAFFICLIGHTSTATE;

}

}

La transition toGreen du diagramme d'états-transitions est traduite en méthode toGreen() qui retourne l'état Green présentant par l'objet de la classe GreenTrafficLightState.

Résultat de l'exécution du code généré:Entry logic.TrafficLightStateEntry logic.GreenTrafficLightState

trafficlight --> toYellow()Exit logic.GreenTrafficLightStateEntry logic.YellowTrafficLightState

trafficlight --> toGreen()Impossible transition

trafficlight --> toRed()Exit logic.YellowTrafficLightStateEntry logic.RedTrafficLightState

Explication:

Quand le feu est jaune (YellowTrafficLightState) et l'objet Context trafficlight reçoit la requête de passer au vert (toGreen). La transition n'a pas lieu, l'état de l'objet Trafficlight est toujours l'instance YellowTrafficLightState. L'exception génère Impossible transition.

42

Page 47: Génération de code à partir d'un diagramme d'états-transitions

5.5.3. Transition complexe

Une transition complexe est une transition avec condition ou activité, ou avec l'aboutissement à un pseudo état comme point de jonction, point de décision, point d'entrée, point de sortie.

Pour illustrer le traitement accordé à la transition complexe, nous utilisons l'exemple de la modélisation d'une porte (Door) de l'OMG UML15. La porte a trois états: ouvert (Open), fermé (Closed), verrouillé (Locked). Pour la fermer, il faut avoir un certain niveau de permission (permitType > 3) et la voie de la porte doit être libre (emptyWay = true).

Le diagramme d'états-transitions:

Les règles de traduction:

− La condition de transition est traduite en condition à l'intérieur de la méthode. Cette méthode est celle de la classe source de la transition (équivalent à l'état source de la transition).

− Le paramètre dans une condition de la transition est considéré comme attribut privé de la classe source de la transition. Les méthodes get et set sont ajoutées pour accéder à cet attribut.

− Chaque activité (effect) de la transition est considérée comme une méthode privée de la classe source de cette transition. Cette méthode privée a le nom de l'activité en question. L'exécution de l'effet aura lieu à l'intérieur de la condition ou à l'intérieur de la méthode (si la condition n'existe pas).

− Quand la transition aboutit à un pseudo état, la transition poursuit sa route jusqu'à la rencontre d'un état (simple ou composite). Toutes les conditions (guard) et les activités (effect) sur cette suite de transitions sont injectées dans la méthode de la classe source (état source de la première transition). L'implication de conditions est ordonnée de l'état source à l'état cible. Cette méthode retourne l'instance de la classe cible (état cible) si toutes les conditions sur cette route sont satisfaites, si

15 OMG UML : OMG Unified Modeling Language, Infrastructure, Version 2.2, February 2009

43

Fig 5.9: Diagramme d'états-transitions de la porte Door

Page 48: Génération de code à partir d'un diagramme d'états-transitions

non retourne la valeur null.

Le diagramme de classes:

La transition complexe ne modifie pas la structure du diagramme de classe, juste le code à l'intérieur des méthodes de classe diffère.

L'extrait de code:

La classe OpenedDoorState contient les attributs permitType, emptyWay provenant des paramètres de condition, la méthode close() avec l'implication des instructions de conditions et la méthode privé show() implémentant l'activité show:

package logic;

public class OpenedDoorState extends AbstractDoorState {private int permitType = 3;private boolean emptyWay = true;public OpenedDoorState() {

super();}

public AbstractDoorState close() {if (getPermitType() > 3) {

if (getEmptyWay() == true) {show();

44

Fig 5.10: Diagramme de classe de la porte Door

Page 49: Génération de code à partir d'un diagramme d'états-transitions

// TO DOreturn DoorInterface.CLOSEDDOORSTATE;

}if (getEmptyWay() == false) {

// TO DOreturn DoorInterface.OPENEDDOORSTATE;

}else return null;

} else return null;}

public int getPermitType() {return permitType;

}

public void setPermitType(int permitType) {this.permitType = permitType;

}

public boolean getEmptyWay() {return emptyWay;

}

public void setEmptyWay(boolean emptyWay) {this.emptyWay = emptyWay;

}

private void show() {//Effet TO DO;

}

}

5.5.4. Etat composite, état initial, état final

Prenons l'exemple précédent et ajoutons l'état composite CheckKey pour montrer comment nous traitons l'état composite, l'état initial, l'état final, l'état historique. Pour déverrouiller la porte, il faut avoir une bonne clé. L'état composite CheckKey présente la procédures de test clé.

Le diagramme d'états-transitions:

45

Fig 5.11: Diagramme d'états-transitions de la porte « Door » ayant l'état composite CheckKey

Page 50: Génération de code à partir d'un diagramme d'états-transitions

Les règles de traduction:

– L'état composite est traduit en classe composite, enfant de la classe AbstractComposite. Cette classe hérite de la classe parent la gestion de ses composants d'état. Elle réécrit les méthodes pour envoyer des requêtes à son composant actif .

– Pour rappel, l'état initial, pseudo état, indique l'état de départ par défaut. Le traitement accordé à ce pseudo état consiste à trouver l'état de départ du diagramme d'états-transitions ou de l'état englobant. Il suffit de trouver l'état initial du diagramme d'états-transitions ou de l'état englobant et puis suit sa transition pour trouver l'état de départ. Il reste à initier l'instance de la classe Context ou de la classe composite avec l'état courant égal à l'état de départ.

– Quand une transition aboutit à l'état final, la recherche de l'état cible de cette transition continue à l'état englobant. La transition sortante de cet état englobant indique l'état cible.

– Comme l'objet Comtext crée tous ses états et garde les références vers ses états simples et ses états composites. Donc le changement d'état courant de l'état composite est conservé, l'historique est conservée.

Le diagramme de classes:

Lorsqu'une instance de la classe composite (CheckKeyDoorState) reçoit une requête, elle délègue la requête à son composant actif. Ce composant actif exécute la requête et retourne un nouveau état. L'instance de la classe composite vérifie si le nouveau état fait partie de ses composants d'état. Si oui, elle change son état courant par le nouveau état

46

Fig 5.12: Diagramme de classe simplifié de la porte Door ayant l'état composite

Page 51: Génération de code à partir d'un diagramme d'états-transitions

et retourne la valeur null à l'instance contenant d'elle. Si non, elle exécute l'action exit et retourne le nouveau état à l'instance contenant d'elle.

L'extrait de code:

La classe CheckKeyDoorState est générée de la même manière que la classe DoorState car chaque diagramme d'états-transitions est considéré comme le développement en détail d'un état composite ou d'une entité.

package logic;

public class CheckKeyDoorState extends CompositeDoorState {public CheckKeyDoorState() {

init(DoorInterface.NOKEYCHECKKEYDOORSTATE);}

public CheckKeyDoorState(AbstractDoorState state) {init(state);

}

private void init(AbstractDoorState state) {setState(state);add(DoorInterface.HASKEYCHECKKEYDOORSTATE);add(DoorInterface.NOKEYCHECKKEYDOORSTATE);

}

public AbstractDoorState checkKey() {return changeState(getState().checkKey());

}

public AbstractDoorState leaveKey() {return changeState(getState().leaveKey());

}

public AbstractDoorState stop() {return changeState(getState().stop());

}

public AbstractDoorState enterKey() {return changeState(getState().enterKey());

}

public AbstractDoorState entry() {super.entry();return getState().entry();

}

}

La méthode init indique que l'instance de la classe CheckKeyDoorState contient deux états instanciés de la classe HasKeyCheckKeyDoorState et NoKeyCheckKeyDoorState.

Le constructeur prend l'instance de la classe NoKeyCheckKeyDoorState pour l'état courant (voir la méthode init) par défaut grâce à l'information fournie par l'état initial via sa transition.

47

Page 52: Génération de code à partir d'un diagramme d'états-transitions

5.5.5. Etat orthogonal

L'état orthogonal modifie juste la gestion des composants de la classe Composite, pas la structure du diagramme de classe. Il faut réécrire la classe Composite pour qu'elle puisse gérer plusieurs états actifs en même temps.

A l'initiation de l'état orthogonal, tous les états qui sont atteints par les états initiaux de cet état composite orthogonal deviennent actifs. Quand un état actif atteint l'état final, il envoie le message exit à l'état composite orthogonal et il est retiré de la liste des composants actifs. L'état orthogonal devient inactif quand son dernier composant actif atteint l'état final. Il faut réécrire la méthode exit() de l'état orthogonal.

5.6.ComparaisonRhapsody [19], JPL, [20] MathWoks16 sont des générateurs utilisant les instructions de condition (witch-case, if-else). Le code généré est compact mais difficile à la compréhension. Ils sont adaptés pour générer des objets ayant un comportement simple.

JCode [21] est un générateur utilisant le mécanisme polymorphique. Il génère le même nombre de classe que notre générateur. Le nombre de ligne de code est tout à fait comparable. L'état composite est interprété en deux manières différentes: soit par le mécanisme de composition pour l'état composite abrégé, soit par le mécanisme d'héritage pour l'état composite non-abrégé tandis que notre générateur a une seule interprétation via le mécanisme de composition. Le code générée par JCode gère le changement d'état de manière décentralisée, est moins robuste que celle de centralisée dans notre projet.

5.7. UtilisationL'interface est très simple, elle se compose des éléments suivants:

− Le champ de texte « Filename » pour indiquer l'url du fichier XMI et

− Le champ de texte « Project name » pour indiquer le nom du projet.

− Le bouton « Choose a file … » pour ouvrir la fenêtre de choix de fichier XMI.

− Le bouton « Generate » pour générer le code.

− La zone de texte pour afficher le contenu du fichier XMI, l'erreur ou le contenu des fichiers java générés.

16 http://www.mathworks.com/products/sfcoder/

48

Page 53: Génération de code à partir d'un diagramme d'états-transitions

Le code généré est enregistré dans le répertoire « workspace »

5.8.Conclusion

Le générateur de code possède trois éléments: Scanner, Analyseur, Générateur. Chaque élément a sa fonction précise et ses responsabilités. La séparation en trois éléments a pour finalité de maîtriser le fonctionnement du générateur de code et de faciliter l'évolution de celui-ci.

Le state pattern, le composite pattern et les règles de codage sont strictement respectés. Ils sont les éléments de bonnes pratiques du génie logiciel qui contribuent à la simplicité, à la lisibilité et à la compréhension du code cible.

La rigueur dans l'approche orienté-objet permet de construire les diagrammes de classe où chaque classe a son vrai rôle, sa vraie responsabilité et que ses collaborations avec les autres ne posent pas de problèmes sémantiques.

Les principaux éléments du diagramme d'états-transitions comme état, événement, transition, point de choix, point de connexion sont traités. Certains détails restent non-traités comme l'élément d'événement temporel et de changement.

49

Fig 5.13: Représentation d'une capture d'écran du générateur de code

Page 54: Génération de code à partir d'un diagramme d'états-transitions

6. CONCLUSION GÉNÉRALECe mémoire de fin d'études présente la réalisation d'un générateur de code, outil de développement de logiciel, à partir d'un diagramme d'états-transitions au travers d'un ensemble d'interrogations et de raisonnements, de sa conception à sa concrétisation.

La modélisation d'objet, si difficile à maîtriser, est la base du développement logiciel. Le diagramme d'états-transitions a été étudié en profondeur dans ses subtilités, ce qui est indispensable à la génération de code. La question de qualité logicielle est prise en compte pour une bonne solution pratique et une suite à ce projet. Le travail pratique a été abordé du général aux détails avec un but de familiariser l'outil qui apporte tant au développement de logiciel et qui est encore considéré comme une technologie sophistiquée.

Le diagramme d'états-transitions, bien adapté à l'approche orienté-objet, offre une vision complète et non-ambiguë du comportement de l'élément auquel il est attaché, mais ne donne pas une vue globale du système. Il est surtout adapté à la phase de réalisation du logiciel. Il forme, avec le state pattern, une nouvelle manière d'organiser le logiciel en minimisant les structures de condition inadaptées à notre cerveau humain.

L'implémentation du générateur de code par module a contribué à la compréhension, à la maîtrise de la réalisation de celui-ci. La rigueur dans l'approche orienté-objet contribue à faciliter la révision du code et à le faire évoluer. L'utilisation du modèle d'états dans le générateur de code cible permet de simplifier le module le plus important du générateur de code et de tester la proposition de code cible.

L'utilisation du state pattern et du composite pattern dans la proposition de code cible permet une solution simple, compréhensible en minimisant les interactions entre les différentes classes pour une structure hiérarchique. Le respect de règles de codage permet la propreté, la lisibilité et la compréhension du code. Le résultat abouti est très encourageant, les principaux éléments du diagramme d'états-transitions sont traités.

Ce qui est intéressant dans le projet de génération de code est la recherche en profondeur de bonnes organisations, de bonnes pratiques et d'une compréhension des exigences dans le développement du logiciel. Le générateur de code n'est pas uniquement un soulagement face à des travaux laborieux, mais aussi une organisation prête à faire face aux évolutions du future. Il est un outil qui évolue avec notre capacité de concevoir des solutions pratiques. L'intelligence collective est très importante pour aboutir à une meilleure solution.

Je suis ouvert à toutes vos questions, vos suggestions.

50

Page 55: Génération de code à partir d'un diagramme d'états-transitions

BIBLIOGRAPHIE[1] Michael R Blaha , James Rumbaugh, Modélisation et conception orientées objet avec UML 2, 2005

[2] Pierre-Alain Muller, Nathalie Gaertner, Modélisation objet avec UML , 2003

[3] Benoit Charoux, Aomar Osmani, Yann Thiery-Mieg, UML 2, 2005

[4] Laurent Audibert, UML 2 : De l'apprentissage à la pratique, 2009

[5] Hugues Bersini, La programmation orientée objet, 2009

[6] OMG, Unified Modeling Language, 2009

[7] Craig Larman, UML 2 et les design patterns, 2005

[8] David Harel, Eran Gery, Executable Object Modeling with Statecharts, 1997

[9] David Harel and Hillel Kugler, The Rhapsody Semantics of Statecharts, 2001

[10] Michelle L. Crane and Juergen Dingel, UML vs. Classical vs. Rhapsody Statecharts:Not All Models are Created Equal, 2005

[11] Alain Abran, James W. Moore, Guide to the Software Engineering Body of Knowledge, 2004

[12] James Gosling, Bill Joy, Guy Steele, Gilad Bracha, The Java™ Language Specification, 2004

[13] Pierre Caboche, Améliorez vos logiciels avec le pattern Etat, 2006

[14] Erich Gamma , Richard Helm , Ralph Johnson , John Vlissides, Design Patterns: Catalogue de modèles de conceptions réutilisables, 2007

[15] Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullman, Compilers: Principles, Techniques, and Tools, 2003

[16] OMG, XML Metadata Interchange, 2007

[17] Elliotte Rusty Harold, Processing XML with Java: a guide to SAX, DOM, JDOM, JAXP, and TrAX, 2002

[18] Hugues Bersini, Object-Oriented Refactoring of Existing Immune Models, 2009

[19] Rhapsody, Code Generation Guide, 2008

[20] Kiri L. Wagstaff, Edward Benowitz, DJ Byrne, Ken Peters, and Garth Watney, Automatic Code Generation For Instrument Flight Software, 2007

[21] Iftikhar Azim Niaz, Automatic Code Generation From UML Class and Statechart Diagrams , 2005

51

Page 56: Génération de code à partir d'un diagramme d'états-transitions

ANNEXESLe lecteur intéressé pourra trouver les codes sources du programme ainsi que les différents diagrammes utilisés pour ce programme au service IRIDIA de la faculté des sciences appliquées de l'Université Libre de Bruxelles.

L'extrait de code de l'exemple TrafficLight :

Le code complet du premier exemple est introduit ici pour que le lecteur puisse apprécier le résultat concret du générateur car c'est trop difficile d'évaluer un générateur sans regarder le respect de convention, des règles de codage.

L'interface TrafficLightInterface déclare les états et les méthodes de l'objet TrafficLight:

package logic;

public interface TrafficLightInterface {public static AbstractTrafficLightState GREENTRAFFICLIGHTSTATE = new

GreenTrafficLightState();public static AbstractTrafficLightState YELLOWTRAFFICLIGHTSTATE = new

YellowTrafficLightState();public static AbstractTrafficLightState REDTRAFFICLIGHTSTATE = new

RedTrafficLightState();

public static AbstractTrafficLightState TRAFFICLIGHTSTATE = new TrafficLightState();

public AbstractTrafficLightState toYellow();public AbstractTrafficLightState toRed();public AbstractTrafficLightState toGreen();

}

La classe AbstractTrafficLightState implémente le comportement par défaut de l'objet TrafficLight:

package logic;

public abstract class AbstractTrafficLightState implements TrafficLightInterface {

public AbstractTrafficLightState toYellow() {return rise();

}

public AbstractTrafficLightState toRed() {return rise();

}

public AbstractTrafficLightState toGreen() {return rise();

}

52

Page 57: Génération de code à partir d'un diagramme d'états-transitions

public AbstractTrafficLightState rise() {System.out.println("Impossible transition");return null;

}

public AbstractTrafficLightState entry() {System.out.println("Entry " + getClass().getName());return null;

}

public AbstractTrafficLightState exit() {System.out.println("Exit " + getClass().getName());return null;

}

}

La classe CompositeTrafficLightState, dérivant de la classe AbstractTrafficLightState, implémente le comportement pour les composants ayant des états (stocke ses états et met en œuvre la gestion de ses états):

package logic;

import java.util.Vector;public abstract class CompositeTrafficLightState extends AbstractTrafficLightState {

private Vector<AbstractTrafficLightState> internalStates = new Vector<AbstractTrafficLightState>();

private AbstractTrafficLightState state = null;public boolean isInternalState(AbstractTrafficLightState state) {

for (AbstractTrafficLightState item : internalStates) {if (item == state) return true;

}return false;

}

public AbstractTrafficLightState changeState(AbstractTrafficLightState target) {

if (target != null) {if (isInternalState(target)) {

state.exit();setState(target);state.entry();return null;

} else {state.exit();return target;

}} else return null;

}

public AbstractTrafficLightState getState() {return state;

}

53

Page 58: Génération de code à partir d'un diagramme d'états-transitions

public void setState(AbstractTrafficLightState state) {this.state = state;

}

public boolean add(AbstractTrafficLightState state) {return internalStates.add(state);

}

public boolean remove(AbstractTrafficLightState state) {return internalStates.remove(state);

}

}

La classe TrafficLightState, classe concrète composite, gère toutes les états de la classe Context TrafficLightState:

package logic;

public class TrafficLightState extends CompositeTrafficLightState {public TrafficLightState() {

init(TrafficLightInterface.GREENTRAFFICLIGHTSTATE);}

public TrafficLightState(AbstractTrafficLightState state) {init(state);

}

private void init(AbstractTrafficLightState state) {setState(state);add(TrafficLightInterface.GREENTRAFFICLIGHTSTATE);add(TrafficLightInterface.YELLOWTRAFFICLIGHTSTATE);add(TrafficLightInterface.REDTRAFFICLIGHTSTATE);

}

public AbstractTrafficLightState toYellow() {return changeState(getState().toYellow());

}

public AbstractTrafficLightState toRed() {return changeState(getState().toRed());

}

public AbstractTrafficLightState toGreen() {return changeState(getState().toGreen());

}

public AbstractTrafficLightState entry() {super.entry();return getState().entry();

}

}

54

Page 59: Génération de code à partir d'un diagramme d'états-transitions

La classe RedTrafficLightState, classe concrète simple, implémente le comportement concret du méthode toGreen():

package logic;

public class RedTrafficLightState extends AbstractTrafficLightState {public RedTrafficLightState() {

super();}

public AbstractTrafficLightState toGreen() {// TO DOreturn TrafficLightInterface.GREENTRAFFICLIGHTSTATE;

}

}

La classe GreenTrafficLightState, classe concrète simple, implémente le comportement concret du méthode toYellow():

package logic;

public class GreenTrafficLightState extends AbstractTrafficLightState {public GreenTrafficLightState() {

super();}

public AbstractTrafficLightState toYellow() {// TO DOreturn TrafficLightInterface.YELLOWTRAFFICLIGHTSTATE;

}

}

La classe YellowTrafficLightState, classe concrète simple, implémente le comportement concret du méthode toRed():

package logic;

public class YellowTrafficLightState extends AbstractTrafficLightState {public YellowTrafficLightState() {

super();}

public AbstractTrafficLightState toRed() {// TO DOreturn TrafficLightInterface.REDTRAFFICLIGHTSTATE;

}

}

55

Page 60: Génération de code à partir d'un diagramme d'états-transitions

La classe GreenTrafficLightState et la classe YellowTrafficLightState sont implémentées de la même manière de la classe RedTrafficLightState.

La classe TrafficLight, classe context du pattern State, elle délègue l'appel à son état:

package logic;

public class TrafficLight implements TrafficLightInterface {private AbstractTrafficLightState state =

TrafficLightInterface.TRAFFICLIGHTSTATE;

public TrafficLight() {state.entry();

}

public AbstractTrafficLightState toYellow() {return state.toYellow();

}

public AbstractTrafficLightState toRed() {return state.toRed();

}

public AbstractTrafficLightState toGreen() {return state.toGreen();

}

}

La classe TrafficLight, client de la classe Context, est juste pour la simulation:

package logic;

public class TrafficLightClient {public static void main(String[] args) {

TrafficLight trafficlight = new TrafficLight();System.out.println("\ntrafficlight --> toYellow()");trafficlight.toYellow();

System.out.println("\ntrafficlight --> toGreen()");trafficlight.toGreen();

System.out.println("\ntrafficlight --> toRed()");trafficlight.toRed();

}

}

56