Upload
duongdien
View
238
Download
3
Embed Size (px)
Citation preview
Object Constraint Language (OCL)Une introduction
Master 2 IFPRU - Module Ingénierie des modèles - FMIN310
Marianne Huchard
7 novembre 2008
1 Présentation générale
OCL est un langage formel, basé sur la logique des prédicats du pre-mier ordre, pour annoter les diagrammes UML en permettant notammentl’expression de contraintes.
1.1 Objectif du langage
Voici les arguments avancés pour l’introduction d’OCL.– Accompagner les diagrammes UML de descriptions :
– précises– non ambiguës
– Eviter cependant les désavantages des langages formels traditionnelsqui sont peu utilisables par les utilisateurs et les concepteurs qui nesont pas rompus à l’exercice des mathématiques :– rester facile à écrire ...– et facile à lire
Dans le cadre de l’ingénierie des modèles, la précision du langage OCLest nécessaire pour pouvoir traduire automatiquement les contraintes OCLdans un langage de programmation afin de les vérifier pendant l’exécutiond’un programme.
1.2 Historique
OCL s’inspire de Syntropy [CD94] méthode basée sur une combinaisond’OMT (Object Modeling Technique) [RBE+97] et d’un sous-ensemble de Z.
A l’origine, OCL a été développé en 1997 par Jos Warmer (IBM), sur lesbases du langage IBEL (Integrated Business Engineering Language). Il a étéformellement intégré à UML 1.1 en 1999.
Nous présentons dans ce document OCL tel qu’il est proposé dans laversion UML 2.0 [OMG03a]. Vous trouverez dans [MG00] une présentationd’OCL (UML 1.4) qui a servi de base à la réalisation de ce support de cours,et dans [Nyt] quelques exemples complémentaires.
1
1.3 Principe
La notion de contrainte Une contrainte est une expression à valeur boo-léenne que l’on peut attacher à n’importe quel élément UML.
Elle indique en général une restriction ou donne des informations com-plémentaires sur un modèle.
Langage déclaratif Les contraintes ne sont pas opérationnelles.On ne peut pas invoquer de processus ni d’opérations autres que des
requêtes.On ne décrit pas le comportement à adopter si une contrainte n’est pas
respectée.
Langage sans effet de bord Les instances ne sont pas modifiées par lescontraintes.
1.4 Utilisation
Les contraintes servent dans plusieurs situations :– description d’invariants sur les classes et les types– pré-conditions et post-conditions sur les opérations et méthodes– contraintes sur la valeur retournée par une opération ou une méthode– règles de dérivation des attributs– description de cibles pour les messages et les actions– expression des gardes (conditions dans les diagrammes dynamiques)– invariants de type pour les stéréotypesLes contraintes servent en particulier à décrire la sémantique d’UML ou
de ses diverses extensions, en participant à la définition des profils.
2 La notion de contexte
Une contrainte OCL est liée à un contexte, qui est le type, l’opération oul’attribut auquel la contrainte se rapporte.
context monContexte <stéréotype> :Expression de la contrainte
Personne
− age : entier
+ setAge(in a : entier)+ getAge():entier {query}
− /majeur : booléen
Fig. 1 – Une classe Personne
Le stéréotype peut prendre les valeurs suivantes :– inv invariant de classe– pre précondition– post postcondition– body indique le résultat d’une opération query
2
– init indique la valeur initiale d’un attribut– derive indique la valeur dérivée d’un attributPar exemple, on peut définir les contraintes suivantes dans le contexte
de la classe Personne de la figure 1, puis dans le contexte de ses méthodessetAge, getAge et pour son attribut age.
context Personne inv :(age <= 140) and (age >=0) - - l’âge est compris entre 0 et 140 ans
On peut placer des commentaires dans les expressions OCL, ils débutentpar deux tirets et se prolongent jusqu’à la fin de la ligne.
context Personne::setAge(a :entier)pre : (a <= 140) and (a >=0) and (a >= age)post : age = a – on peut écrire également a=age
context Personne::getAge() :entierbody : age
context Personne::age :entierinit : 0
context Personne::majeur :booléenderive : age>=18
Version visuelle La figure 2 présente une version alternative, graphique,de la contrainte sur l’âge de la personne.
Personne
− age : entier
+ setAge(in a : entier)
{<<invariant>>(age<=140
and age >=0) }
Fig. 2 – Une classe Personne
Nommage de la contrainte
context Personne inv ageBorné :(age <= 140) and (age >=0) - - l’âge ne peut depasser 140 ans
Utilisation du mot-clef self pour désigner l’objetNotez le caractère . qui offre la navigation (accès) à l’attribut.
context Personne inv :(self.age <= 140) and (self.age >=0) - - l’âge ne peut depasser 140 ans
3
Utilisation d’un nom d’instance formel
context p :Personne inv :(p.age <= 140) and (p.age >=0) - - l’age ne peut depasser 140 ans
Question 2.1 Ajoutez un attribut mère de type Personne dans la classePersonne. Ecrivez une contrainte précisant que la mère d’une personne nepeut être cette personne elle-même et que l’âge de la mère doit être supérieurà celui de la personne.
Réponse 2.1
context Personne inv :self.mère <> self and self.mère.age > self.age
3 Types OCL
La figure 3 présente la hiérarchie des types en OCL. Nous les décrironsprogressivement dans cette section et dans les suivantes.
OclAny
Réel Booléen String
du modèle
CollectionOclState
OclVoid
types de baseprédéfinis
Entier
OclType OclModelElement
types et éléments
Tuple
Bag SequenceOrderedSetSet
............
Fig. 3 – La hiérarchie des types en OCL
3.1 Types de base
Les types de base prédéfinis sont les suivants.– Entier (Integer)– Réel (Real)– String– Booléen (Boolean)Quelques types spéciaux s’y ajoutent, en particulier :– OclModelElement (énumération des éléments du modèle)– OclType (énumération des types du modèle)– OclAny (tout type autre que Tuple et Collection)– OclState (pour les diagrammes d’états)– OclVoid sous-type de tous les types
4
Entieropérateurs = <> + - * / abs div mod max min < > <= >=- est unaire ou binaire
Réelopérateurs = <> + - * / abs floor round max min < > <= >=- est unaire ou binaire
Stringopérateurs = size() concat(String) toUpper() toLower()substring(Entier, Entier)Les chaînes de caractères constantes s’écrivent entre deux simples quotes :’voici une chaîne’
Booléenopérateurs = or xor and notb1 implies b2if b then expression1 else expression2 endif
Quelques exemples d’utilisation des opérateurs booléens, sur la figure 4.
− age : entier− majeur : Booléen− marié : Booléen− catégorie : enum {enfant,ado,adulte}
Personne
Fig. 4 – La classe Personne précisée
context Personne inv :marié implies majeur
context Personne inv :if age >=18 then majeur=vraielse majeur=fauxendif
Cette dernière contrainte peut être plus concise (nous l’avons même déjàécrite avec derive) :
context Personne inv :majeur = age >=18
5
précédence des opérateurs
. ->not - unaire
* /+ -
if then else< > <= >=
<> =and or xor
implies
3.2 Types énumérés
Leurs valeurs apparaissent précédées de #. Par exemple, pour donner uninvariant supplémentaire pour la figure 4, nous pourrions écrire :
context Personne inv :if age <=12 then catégorie =#enfantelse if age <=18 then catégorie =#ado
else catégorie=#adulteendif
endif
Cet invariant peut aussi s’écrire avec derive.
3.3 Types des modèles
Les types des modèles utilisables en OCL sont les « classificateurs »,notamment les classes, les interfaces et les associations.
On peut écrire en particulier des expressions impliquant des objets dontles types sont reliés par une relation de spécialisation, grâce aux opérateurs :
– oclAsType(t) (conversion ascendante ou descendante de type vers t) ;la conversion ascendante sert pour l’accès à une propriété redéfinie ; laconversion descendante sert pour l’accès à une nouvelle propriété.
– oclIsTypeOf(t) (vrai si t est supertype direct)– oclIsKindOf(t) (vrai si t est supertype indirect)Dans le cadre de la figure 5, nous pourrions écrire par exemple les ex-
pressions (r est supposé désigner une instance de Rectangle) :– p = r– p.oclAsType(Rectangle)– r.oclIsTypeOf(Rectangle) (vrai)– r.oclIsKindOf(Polygone) (vrai)
Question 3.1 En supposant l’existence d’un attribut hauteur dans la classeRectangle et d’une méthode hauteur() :Réel dans Polygone, écrivez unecontrainte dans Polygone disant que le résultat de hauteur() :Réel vauthauteur pour les polygones qui sont des rectangles, sinon 0. Remarquez quec’est un exemple de mauvaise conception objet mais une excellente illustra-tion des opérateurs présentés ci-dessus !
6
Polygone
Rectangle
p : Polygoner : Rectangle
Fig. 5 – Polygones et rectangles
Réponse 3.1
context Polygone::hauteur() post :if self.oclIsKindOf(Rectangle)then result=self.oclAsType(Rectangle).hauteurelse result=0endif
4 Navigation dans les modèles
4.1 Accès aux attributs
L’accès (navigation) vers un attribut s’effectue en mentionnant l’attribut,comme nous l’avons déjà vu, derrière l’opérateur d’accès noté ’.’
Personne
− age : Entier
+ getAge():Entier{query}
Voiture
− propriétaire : Personne
Fig. 6 – Personnes et voitures
Pour la classe Voiture de la figure 6, on peut ainsi écrire la contrainteci-dessous.
context Voiture inv propriétaireMajeur :self.propriétaire.age >= 18
Question 4.1 Ecrivez pour le diagramme de la figure 7 la contrainte quicaractérise l’attribut dérivé carteVermeil. Un voyageur a droit à la cartevermeil si c’est une femme de plus de 60 ans ou un homme de plus de 65ans.
Réponse 4.1
context Voyageur inv :carteVermeil = ((age >=65) or ((sexe=#féminin) and (age >=60)))
Cette contrainte peut également s’ecrire avec derive.
7
Voyageur
nom:Stringage:int
Sexe<<enumeration>>
masculinfémininsexe:Sexe
/ carteVermeil:Boolean
Fig. 7 – Voyageur
4.2 Accès aux opérations query
Il se fait avec une notation identique (utilisation du ’.’).Pour la classe Voiture de la figure 6, on peut ainsi réécrire la contrainte
propriétaireMajeur.
context Voiture inv :self.propriétaire.getAge() >= 18
4.3 Accès aux extrémités d’associations
La navigation le long des liens se fait en utilisant :– soit les noms de rôles– soit les noms des classes extrémités en mettant leur première lettre en
minuscule, à condition qu’il n’y ait pas ambiguïtéPour la classe Voiture de la figure 8, on peut ainsi réécrire la contrainte
propriétaireMajeur.
Personne
− age : Entier
+ getAge():Entier{query}
Voiturepossède1 *
propriétaire
Fig. 8 – Personnes et voitures (avec association)
context Voiture inv :self.propriétaire.age >= 18
context Voiture inv :self.personne.age >= 18
Question 4.2 Donnez un exemple où l’utilisation du nom de classe conduità une ambiguïté.
Réponse 4.2 Deux associations entre Voiture et Personne (possède et aCon-duit)
Question 4.3 (Exercice tiré de [Nyt]) Soient deux contraintes associéesau diagramme de la figure 9 :
context C1 inv :c2.attr2=c2.c3.attr3
8
C1
attr1:Boolean
C2
attr2:Boolean
C3
attr3:Boolean0..1 0..*
Fig. 9 – Contraintes ...
context C2 inv :attr2=c3.attr3
Pensez-vous que les deux contraintes soient équivalentes ?
Réponse 4.3 La première dit que pour des instances de C2 et C3 liées avecune instance de C1 les attributs attr2 et attr3 sont égaux, mais n’impose rienà des instances de C2 et C3 non liées à une instance de C1 (et il y en a àcause de la multiplicité).
La deuxième dit que pour tout couple d’instances de C2 et C3 liées, cesdeux attributs sont égaux.
4.4 Navigation vers les classes association
Pour naviguer vers une classe association, on utilise le nom de la classe(en mettant le premier caractère en minuscule). Par exemple dans le cas dudiagramme de la figure 10 1 :
context p :Personne inv :p.contrat.salaire >= 0
Ou encore, on précise le nom de rôle opposé (c’est même obligatoire pourune association réflexive) :
context p :Personne inv :p.contrat[employeur].salaire >= 0
Entreprise
Contrat
typeContratdateEmbauchesalaire
emploie
employeur employé
0..1 *
encadre
responsablePersonne 0..1
évaluationsdateDébut
0..1 agentSecrétariat
Encadrement
Fig. 10 – Emploi
Pour naviguer depuis une classe association, on utilise les noms des rôlesou de classes. La navigation depuis une classe association vers un rôle ne peutdonner qu’un objet. Dans l’exemple ci-dessous, c est un lien, qui associe pardéfinition une seule entreprise à un seul employé.
context c :Contrat inv :c.employé.age >= 16
1en UML 2, contrairement à UML 1.xx, le nom de la classe d’association et le nom del’association devraient être identiques car il s’agit d’un même élément UML ; le diagrammeest donc ici écrit en UML 1.xx
9
Question 4.4 Dans la même figure 10, exprimez par une contrainte :– le fait que le salaire d’un agent de secrétariat est inférieur à celui de
son responsable ;– un agent de secrétariat a un type de contrat "agentAdministratif" ;– un agent de secrétariat a une date d’embauche antérieure à la date de
début de l’encadrement (on suppose que les dates sont des entiers) ;– écrire la dernière contrainte dans le contexte Personne.
Réponse 4.4 Le salaire d’un agent de secrétariat est inférieur à celui deson responsable.
context e :encadrement inv :e.responsable.contrat.salaire >= e.agentSecrétariat.contrat.salaire
Un agent de secrétariat a un type de contrat "agentAdministratif".
context e :encadrement inv :e.agentSecrétariat.contrat.typeContrat=’agentAdministratif ’
Un agent de secrétariat a une date d’embauche antérieure à la date dedébut de l’encadrement (on suppose que les dates sont des entiers).
context e :encadrement inv :e.agentSecrétariat.contrat.dateEmbauche <= e.dateDebut
L’écrire dans le contexte Personne ...
context p :Personne inv :p.agentSecrétariat.contrat.dateEmbauche
<= p.encadrement[agentSecrétariat].dateDebut
Dans ce dernier cas, pour être rigoureux il faudrait vérifier de manièrepréliminaire que p.agentSecrétariat est valué, mais nous ne verrons que plusloin dans le cours comment faire.
5 Eléments du langage
5.1 Désignation de la valeur antérieure
Dans une post-condition, il est parfois utile de pouvoir désigner la valeurd’une propriété avant l’exécution de l’opération. Ceci se fait en suffixant lenom de la propriété avec @pre.
Par exemple, pour décrire une nouvelle méthode de la classe Personne :
context Personne::feteAnniversaire()pré : age < 140post : age = age@pre + 1
10
5.2 Définition de variables et d’opérations
On peut définir des variables pour simplifier l’écriture de certaines ex-pressions. On utilise pour cela la syntaxe :
let variable : type = expression1 inexpression2
Par exemple, avec l’attribut dérivé impot dans Personne, on pourraitécrire :
context Personne inv :let montantImposable : Réel = contrat.salaire*0.8 inif (montantImposable >= 100000)then impot = montantImposable*45/100else if (montantImposable >= 50000)
then impot = montantImposable*30/100else impot = montantImposable*10/100endif
endifLà encore, il serait plus correct de vérifier tout d’abord que contrat est
valué.Si on veut définir une variable utilisable dans plusieurs contraintes de la
classe, on peut utiliser la construction def.
context Personnedef : montantImposable : Réel = contrat.salaire*0.8
Lorsqu’il est utile de définir de nouvelles opérations, on peut procéderavec la même construction def.
context Personnedef : ageCorrect(a :Réel) :Booléen = a>=0 and a<=140
Une telle définition permet de réécrire la pré-condition de l’opérationsetAge et l’invariant sur l’âge d’une personne de manière plus courte.
context Personne::setAge(a :entier)pre : ageCorrect(a) and (a >= age)
context Personne inv :ageCorrect(age) - - l’âge ne peut depasser 140 ans
5.3 Retour de méthode
L’objet retourné par une opération est désigné par result.
context Personne::getAge()post : result=age
Lorsque la postcondition se résume à décrire la valeur du résultat c’estdonc équivalent à l’utilisation de body.
11
Question 5.1 Proposez une classe Etudiant, disposant de 3 notes et munied’une opération mention qui retourne la mention de l’étudiant sous formed’une chaîne de caractères. Ecrivez les contraintes et en particulier utilisezle let pour écrire la postcondition de mention.
Réponse 5.1
context Etudiant::mention() post :let moyenne : Real =(note1+note2+note3)/3 inif (moyenne >= 16)then result=’très bien’else if (moyenne >= 14)
then result=’bien’else result=’moins bien’endif
endif
6 Collections
Collection
SequenceBagSet OrderedSet
Ensemblemathématique
ordonnémathématiqueEnsemble Famille Famille
ordonnée
Fig. 11 – Les types de collections
6.1 Types de collections
La figure 11 présente les cinq types de collections existant en OCL :– Collection est un type abstrait– Set correspond à la notion mathématique d’ensemble– OrderedSet correspond à la notion mathématique d’ensemble ordonné– Bag correspond à la notion mathématique de famille (un élément peut
y apparaître plusieurs fois)– Sequence correspond à la notion mathématique de famille, et les élé-
ments sont, de plus, ordonnés.On peut définir des collections par des littéraux de la manière suivante :– Set { 2, 4, 6, 8 }– OrderedSet { 2, 4, 6, 8 }– Bag { 2, 4, 4, 6, 6, 8 }– Sequence { ’le’, ’chat’, ’boit’, ’le’, ’lait’ }– Sequence { 1..10 } spécification d’un intervalle d’entiersLes collections peuvent avoir plusieurs niveaux, il est possible de définir
des collections de collections, par exemple : Set { 2, 4, Set {6, 8 }}
12
6.2 Les collections comme résultats de navigation
Set On obtient naturellement un Set en naviguant le long d’une associa-tion ordinaire (ne mentionant pas bag ou seq à l’une de ses extrémités).Par exemple, pour la figure 10, l’expression self.employé dans le contexteEntreprise est un Set.
Bag La figure 12 illustre une navigation permettant d’obtenir un Bag : c’estle résultat de l’expression self.occurrence.mot dans le contexte Texte.Cette expression est, par ailleurs, un raccourci pour une notation que nousverrons ci-après.
Texte Occurrence
Mot
*
1
*
Fig. 12 – Occurrences de mots dans un texte
OrderedSet On peut obtenir un ensemble ordonné en naviguant vers uneextrémité d’association munie de la contrainte ordonné, par exemple avecl’expression self.personne dans le contexte FileAttente.
FileAttente Personne{ordonné}
0..1 *
Fig. 13 – File d’attente
Sequence On peut obtenir une séquence en naviguant vers une extré-mité d’association munie de la contrainte seq, par exemple avec l’expressionself.mot dans le contexte Texte de la figure 14.
{seq}MotTexte
**
Fig. 14 – Une autre représentation des textes
Question 6.1 Qu’obtient-on en accédant à un attribut multi-valué ?
Réponse 6.1 (Question 6.1) On obtient un bag si une valeur peut êtrerépétée plusieurs fois ; une séquence si les valeurs sont de plus ordonnées ; unset s’il n’y a jamais de doublon dans les valeurs ; un orderedSet si les valeurssont de plus ordonnées.
13
6.3 Opérations de Collection
Notez que les langages de transformation de modèles qui voient le jouractuellement disposent d’opérateurs assez semblables à ceux proposés parOCL, à des fins de parcours des modèles.
6.3.1 Remarques générales
Dans la suite, nous considérons des collections d’éléments de type T.
Appel Les opérations sur une collection sont généralement mentionnéesavec ->
Itérateurs Les opérations qui prennent une expression comme paramètrepeuvent déclarer optionnellement un itérateur, par exemple toute opérationde la forme operation(expression) existe également sous deux formes pluscomplexes.operation(v | expression-contenant-v)operation(v : T | expression-contenant-v)
6.3.2 Opérations sur tous les types de collections
isEmpty() :BooleannotEmpty() :BooleanPermettent de tester si la collection est vide ou non. Ils servent en particulierà tester l’existence d’un lien dans une association dont la multiplicité inclut0. Pour revenir sur l’exemple des employés (dernière question) :
context p :Personne inv :p.agentSecretariat->notEmpty() implies – p est un responsablep.agentSecrétariat.contrat.dateEmbauche
<= p.encadrement[agentSecrétariat].dateDebut
size() :EntierDonne la taille (nombre d’éléments).
count(unObjet :T) :EntierDonne le nombre d’occurrences de unObjet.
sum() :TAddition de tous les éléments de la collection (qui doivent supporter uneopération + associative)
Nous pouvons par exemple définir une variable représentant l’âge moyendes employés d’une entreprise.
context e :Entreprisedef : ageMoyen : Real = e.employé.age->sum() / e.employé.age->size()
Ou encore, pour une classe Etudiant généralisée, disposant d’un attributnotes :Real[*], on peut définir la variable moyenne.
context e :Etudiantdef : moyenne : Real = e.notes->sum() / e.notes->size()
14
includes(o :T) :Booleanexcludes(o :T) :BooleanRetourne vrai (resp. faux) si et seulement si o est (resp. n’est pas) un élémentde la collection.
Toujours pour la classe Etudiant généralisée, disposant d’un attributnotes :Real[*], on peut définir l’opération estEliminé() :Boolean. Unétudiant est éliminé s’il a obtenu un zéro.
context e :Etudiantdef : estEliminé() : Boolean = e.notes->includes(0)
includesAll(c :Collection(T)) :BooleanexcludesAll(c :Collection(T)) :BooleanRetourne vrai (resp. faux) si et seulement si la collection contient tous les(resp. ne contient aucun des) éléments de c.
forAll(uneExpression :Boolean) :BooleanVaut vrai si et seulement si uneExpression est vraie pour tous les élémentsde la collection.
Dans une entreprise, une contrainte est que tous les employés aient plusde 16 ans.
context e :Entreprise inv :e.employe->forAll(emp :Personne | emp.age >= 16)
Cette opération a une variante permettant d’itérer avec plusieurs itérateurssur la même collection. On peut ainsi parcourir des ensembles produits de lamême collection.
forAll(t1,t2 :T | expression-contenant-t1-et-t2)En supposant que la classe Personne dispose d’un attribut nom, on peut
admettre que deux employés différents n’ont jamais le même nom.
context e :Entreprise inv :e.employe->forAll(e1,e2 :Personne | e1 <> e2 implies e1.nom <> e2.nom)
exists(uneExpression :Boolean)Vrai si et seulement si au moins un élément de la collection satisfait uneExpression.
Dans toute entreprise, il y a au moins une personne dont le contrat estde type "agent administratif".
context e :Entreprise inv :e.contrat->exists(c :Contrat | c.typeContrat=’agent administratif’)
iterate(i :T ; acc :Type = uneExpression |ExpressionAvecietacc) :Cette opération permet de généraliser et d’écrire la plupart des autres.i est un itérateur sur la collectionacc est un accumulateur initialisé avec uneExpressionL’expression ExpressionAvecietacc est évaluée pour chaque i et son résultatest affecté dans acc. Le résultat de iterate est acc.
Cette opération permet dans l’exemple ci-dessous de caractériser le ré-sultat d’une hypothétique opération masseSalariale d’une entreprise.
15
context Entreprise::masseSalariale() :Real post :result = employé->iterate(p :Personne ; ms :Real=0 | ms+p.contrat.salaire)
Question 6.2 Ecrire size et forAll dans le contexte de la classe Collec-tion.
Réponse 6.2 (Question 6.2) Voici comment ces méthodes sont spécifiéesdans [OMG01].
context Collection : :size() post :result = self->iterate(elem ; acc :Integer=0| acc+1)
context Collection : :forAll(expr) post :result = self->iterate(elem ; acc :Boolean=true| acc and expr)
isUnique(uneExpression :BooleanExpression) :BooleanVrai si et seulement si uneExpression s’évalue avec une valeur distincte pourchaque élément de la collection.
sortedBy(uneExpression) :SequenceRetourne une séquence contenant les éléments de la collection triés par ordrecroissant suivant le critère uneExpression. L’évaluation de uneExpressiondoit donc supporter l’opération <.
Question 6.3 Ecrire la post condition d’une méthode salariésTriés dansle contexte de la classe Entreprise. Cette méthode retourne les employésordonnés suivant leur salaire.
Réponse 6.3 (Question 6.3)
context Entreprise : : salariesTries() :OrderedSet(Personne) post :result = self.employe->sortedBy(p | p.contrat.salaire)
any(uneExpression :OclExpression) :TRetourne n’importe quel élément de la collection qui vérifie uneExpression.
one(uneExpression :OclExpression) :BooléenVrai si et seulement si un et un seul élément de la collection vérifie uneExpression.
6.3.3 Opérations sur tous les types de collections, définies spéci-fiquement sur chaque type
Le lecteur doit être averti du fait que la présentation des opérateursproposée ci-dessous privilégie la concision de l’exposé. Certaines opérationsont des signatures plus précises que celles mentionnées ici. Par exempleselect, commune aux trois types de collection concrets, n’est pas définiedans Collection, mais seulement dans les types concrets avec des signa-tures spécifiques :
16
select(expr :BooleanExpression) : Set(T)select(expr :BooleanExpression) : OrderedSet(T)select(expr :BooleanExpression) : Bag(T)select(expr :BooleanExpression) : Sequence(T)
Dans un tel cas, nous présentons une généralisation de ces opérationsselect(expr :BooleanExpression) : Collection(T)Le lecteur est renvoyé à la documentation OCL [OMG03a] pour les signaturesprécises.
select(expr :BooleanExpression) : Collection(T)reject(expr :BooleanExpression) : Collection(T)Retourne une collection du même type construite par sélection des élémentsvérifiant (resp. ne vérifiant pas) expr.
L’expression représentant dans le contexte d’une entreprise les employés âgésde plus de 60 ans serait la suivante :self.employé->select(p :Personne|p.age>=60)
collect(expr :BooleanExpression) : Collection(T)Retourne une collection composée des résultats successifs de l’application deexpr à chaque élément de la collection.
La manière standard d’atteindre les mots d’un texte s’écrit ainsi dans lecontexte d’un texte (Figure 12) :self.occurrence->collect(mot)self.occurrence->collect(o | o.mot)self.occurrence->collect(o :Occurrence | o.mot)Comme nous l’avons évoqué précédemment, cette notation admet un rac-courci :self.occurrence.mot
including(unObjet :T) :Collection(T)excluding(unObjet :T) :Collection(T)Retourne une collection résultant de l’ajout (resp. du retrait) de unObjet àla collection.
On peut ainsi décrire la post-condition d’une opération embauche(p :Personne)de la classe Entreprise.
context Entreprise : :embauche(p :Personne) post :self.employé=self.employé@pre->including(p)
opération =
union(c :Collection) :Collection
17
6.4 Opérations de conversion
Chaque sorte de collection (set, orderedSet, sequence, bag) peut êtretransformée dans n’importe quelle autre sorte.
Par exemple un Bag peut être transformé en :– sequence par l’opération asSequence() :Sequence(T) ; l’ordre résul-
tant est indéterminé– ensemble par l’opération asSet() :Set(T) ; les doublons sont éliminés– ensemble ordonné par l’opération asOrderedSet() :OrderedSet(T) ;
les doublons sont éliminés ; l’ordre résultant est indéterminé.
A titre d’illustration, si nous voulons exprimer le fait que le nombre de motsdifférents d’un texte est plus grand que deux (figure 12) :
context Texte inv :self.occurrence->collect(mot)->asSet()->size() >= 2
6.5 Opérations propres à Set et Bag
– intersection
6.6 Opérations propres à OrderedSet et Sequence
– ajout d’un objet à la fin– append(unObjet : T) : OrderedSet(T) pour les ensembles ordon-
nés– append(unObjet : T) : Sequence(T) pour les séquences
– ajout d’un objet au début– prepend(unObjet : T) : OrderedSet(T) pour les ensembles or-
donnés– prepend(unObjet : T) : Sequence(T) pour les séquences
– objets d’un intervalle– subOrderedSet(inf :Entier, sup :Entier) : OrderedSet(T) pour
les ensembles ordonnés– subSequence(inf :Entier, sup :Entier) : Sequence(T) pour les
séquences– at(inf :Entier) : T– first() : T– last() : T– insertion d’un objet à un certain indice
– insertAt(i :Integer, o :T) : OrderedSet(T) pour les ensemblesordonnés
– insertAt(i :Integer, o :T) : Sequence(T) pour les séquences– indexOf(o :T) : Integer
Question 6.4 Ecrire la post-condition de sortedBy
6.7 Relation de « conformance de type »
La figure 15 présente les relations de conformance de type prévues enOCL.
Si la classe Assiette est sous-classe de Vaisselle, on peut en déduire :
18
Collection(X)
Sequence(X)
Sous−types
Type2 Set(Type2)
Type1 Set(Type1)
Sous−types Sous−types
OrderedSet(X) Bag(X)Set(X)
Fig. 15 – Sous-typage de types paramétrés
– Set(Assiette) se conforme à Collection(Assiette)– Set(Assiette) se conforme à Set(Vaisselle)– Set(Assiette) se conforme à Collection(Vaisselle)OCL parle même de relation de sous-typage. Nous étudions la question
de la substituabilité sur un bref exemple qui peut être généralisé et quiillustre le fait que, les collections étant constantes, la conformance rejoint icila substituabilité.
Prenons le cas d’un Set(Assiette). Considéré comme un Set(Vaisselle),on peut lui appliquer l’opérationincluding(object :Vaisselle) :Set(Vaisselle)où object peut être tout à fait autre chose qu’une assiette. Ce faisant on créeet retourne un Set(Vaisselle) (et non un Set(Assiette)), ce qui n’est pasproblématique (il n’y a pas d’erreur de type, on n’a pas introduit un verredans un ensemble d’assiettes).
7 Utilisation d’OCL pour les gardes et les actions
De manière générale, OCL est un langage de navigation qui peut êtreutilisé dans différents points des diagrammes UML. Nous allons illustrercet aspect d’OCL dans le cadre des diagrammes d’états, pour exprimer desgardes et préciser des actions.
7.1 Gardes
Les gardes sont des expressions booléennes utilisées dans le cadre desdiagrammes UML basés sur les automates d’état finis : diagrammes d’étatset diagrammes d’activités.
Elles conditionnent le déclenchement d’une transition lors de l’occurrenced’un événement. En UML elles sont notées entre crochets.
Les diagrammes d’états modélisent le comportement d’éléments UML(le plus souvent le comportement d’une instance d’une classe donnée). Ils secomposent d’états et de transitions.
Un état est un moment de la vie d’une instance déterminé par le faitqu’elle vérifie une condition particulière, est en train d’effectuer une opérationou attend un événement. Il est représenté en UML par une boîte aux anglesarrondis.
Une transition représente le passage d’un état à un autre. Ce passage estdéclenché par un événement : une modification (une condition sur l’instancedevient vraie), l’appel d’une opération, la réception d’un signal asynchrone,la fin d’une période de temps. Les gardes sont associées aux transitions, ellessont évaluées lorsqu’un événement survient.
19
CompteBancaire
− solde : Réel
+ déposer(m:Réel)+ retirer (m:Réel)+ fermer()
Banque
− fraisDécouvert:Réel
1* gère
− découvertMax:Réel
Fig. 16 – Comptes bancaires
La figure 17, tirée de [OMG00], présente un diagramme d’états simplepour un compte bancaire dont le diagramme de classes est décrit sur la figure16. Un compte bancaire peut avoir deux états, ouvert ou fermé. Il passede l’état ouvert à l’état fermé lorsque l’opération fermer() est appelée surl’objet. Il reste dans l’état ouvert lorsque deux opérations, respectivementdéposer et retirer sont appelées. La garde qui suit l’opération retirer indiqueque cette dernière n’a d’effet que si le montant retiré est positif et inférieurau solde du compte auquel on ajoute une somme représentant le découvertautorisé.
fermer()
déposer(m)
and m>=0]
FerméOuvert
retirer(m)[m<=solde+banque.découvertMax
Fig. 17 – Etats principaux d’un compte bancaire
7.2 Actions
OCL peut être utilisé également dans la description d’actions qui éti-quettent les transitions. Ces actions prennent place dans l’étiquette derrièrele symbole /. Dans ce cas on se sert d’OCL comme d’un langage de naviga-tion, mais on n’écrit pas forcément une expression booléenne.
La figure 18 décrit ainsi les sous-états de Ouvert. Un compte ouvert estdébiteur ou créditeur. Tout dépôt sur un solde débiteur entraîne le retraitd’une somme forfaitaire pour frais de gestion des découverts. Le retrait estimpossible sur un compte débiteur. Dans la situation où le compte est cré-diteur, un retrait d’une somme supérieure au solde sans dépassement dudécouvert autorisé fait passer le compte dans l’état débiteur. Inversement,lorsque le compte est débiteur, le dépôt d’une somme supérieure au débit(augmenté des frais de découvert) fait passer le compte dans l’état créditeur.
Les actions sont exprimées à l’aide du symbole := et d’expressions denavigation en OCL, comme par exemple l’accès à l’attribut fraisDécouvert.
20
Le diagramme d’états étant naturellement décrit pour une instance d’uneclasse (compte bancaire), ces expressions de navigation sont écrites commesi nous nous trouvions dans context CompteBancaire.
Débiteur Créditeur
déposer(m)
/ solde := solde +m− banque.fraisDécouvert
déposer(m)
déposer(m)
[m>=0]/ solde := solde+m
/ solde := solde − m
[m<=solde and m>=0]retrait(m)
/ solde := solde − mm<=solde+découvertMax][m > solde and retirer(m)
and m>=0]
/ solde := solde+m − banque.fraisDécouvert[m>=abs(solde+banque.fraisDécouvert)]
Ouvert
[m<abs(solde+banque.fraisDécouvert)
Fig. 18 – Etat ouvert d’un compte bancaire
Alternativement ou parallèlement, une partie des informations portéessur le diagramme d’états peut apparaître dans le diagramme de classes. Lafigure 19 présente ainsi des précisions sur le diagramme de la figure 16. Lescontraintes OCL sont présentées de manière graphique dans des notes. L’étatapparaît comme un attribut (étatSolde) de la classe CompteBancaire. Lesgardes du diagramme d’états correspondent dans le diagramme de classesà des préconditions ou à des conditions dans la post-condition, tandis queles actions sont associées à l’écriture de la post-condition. Cet exemple doitaussi nous inviter à réfléchir au problème de la cohérence entre diagrammes.En effet, les informations que portent le diagramme d’états et le diagrammede classes se doivent d’être en accord.
8 OCL dans le méta-modèle UML
Dans la première partie du cours, nous avons abordé le méta-modèleUML qui est une description du langage d’expression des modèles. La des-cription du méta-modèle utilise le même formalisme que celle des modèles.Les éléments du méta-modèle sont les méta-classes décrivant les classes, lesattributs, les associations, les opérations, les composants : Class, Attribute,Operation, etc. Les éléments d’un modèle sont des instances de ces méta-classes.
Dans la documentation qui décrit le méta-modèle UML, les contraintessont utilisées pour préciser des invariants sur les méta-classes et notammentpour décrire des extensions du méta-modèle à l’aide de stéréotypes. Notezcependant que ces règles ne décrivent pas toute la sémantique UML. Unepartie de cette sémantique reste écrite en langue naturelle, et une partien’est pas décrite du tout.
Dans cette partie nous allons en donner quelques exemples liés aux pro-priétés, à la relation de spécialisation/généralisation, aux associations et en-fin aux contraintes.
21
+ déposer(m:Réel)+ retirer (m:Réel)+ fermer()
− solde : Réel− étatSolde : enum{créditeur,débiteur}
CompteBancaire
m>=0{<<précondition>>
then soldeif (étatSolde@pre=#créditeur)
=solde@pre+m
(
<<postcondition>>
}
else −− étatSolde@pre=#débiteur (m > abs(Solde) implies étatSolde=#créditeur)
and solde=solde@pre+m−banque.fraisDécouvert
Banque
− fraisDécouvert:Réel
1* gère
− découvertMax:Réel
{<<invariant>> (solde<0 and étatSolde=#débiteur)or (solde>=0 and étatSolde=#créditeur)}
Fig. 19 – Comptes bancaires détaillés
Le document utilisé est [OMG03b]. Nous ajoutons la partie context pourplus de clarté.
8.1 Contraintes sur les propriétés
Règle 8.1Quelques contraintes établissent des règles sur la multiplicité (Figure 10 duméta-modèle [OMG03b]).
context MultiplicityElement inv :lower = lowerBound()lowerBound()->notEmpty() implies lowerBound() >= 0lowerBound()->notEmpty() and upperBound()->notEmpty()implies upperBound() >= lowerBound()
context MultiplicityElement : :lowerBound() : Integer inv :result=if lowerValue->isEmpty() then 1
else lowerValue().integerValue() endif
upperBound() est définie de manière similaire.
8.2 Contraintes sur la spécialisation/généralisation
Nous montrons tout d’abord comment les super-classes directes d’uneclasse sont obtenues (figure 22 dans [OMG03b]).
context Classifier::parents() :Set(Classifier) post :result = self.generalization.parent
22
Cette opération se généralise facilement pour obtenir toutes les super-classes d’une classe.
context Classifier::allParents() :Set(Classifier) post :result = self.parents()->union(self.parents()->collect(p | p.allParents()))
Règle 8.2La relation de spécialisation/généralisation ne peut induire un circuit.
context Classifier inv :not self.allParents() -> includes(self)
8.3 Contraintes sur les associations
Les associations sont décrites dans la figure 30 du document [OMG03b].
Règle 8.3endType est dérivé à partir des types des memberEnd
context Association inv :self.endType = self.memberEnd -> collect(e | e.type)
La multiplicité dans une composition ne peut dépasser 1.
context Property inv :isComposite implies (upperBound()->isEmpty() or upperBound() <= 1)
8.4 Contraintes sur les contraintes
D’après le diagramme 14 du méta-modèle UML [OMG03b].
Règle 8.4Une contrainte ne peut pas s’appliquer à elle-même.
context Constraint inv :not constrainedElement->include(self)
Sur ce diagramme, nous voyons apparaître une limitation d’OCL dansl’expression des contraintes. La sémantique voudrait notamment que :
– la spécification de valeur pour une contrainte soit de type booléen ;– l’évaluation d’une contrainte ne devrait pas avoir d’effet de bord.D’après la documentation, ces deux contraintes ne peuvent être exprimées
en OCL.
23
Références
[CD94] Steve Cook and John Daniels. Designing Object Systems :Object-Oriented Modelling with Syntropy. Prentice Hall,http ://www.syntropy.co.uk/syntropy/, 1994. ISBN 0-13-203860-9.
[MG00] Pierre-Alain Muller and Nathalie Gaertner. Modélisation objetavec UML. Eyrolles, 2000.
[Nyt] Jan Pettersen Nytun. OCL, The Object Constraint Language.Slides. http :// fag.grm.hia.no/ ikt2340/ year2002/ themes/ exe-cutableUML/ executableUML.html.
[OMG00] OMG. Unified modeling language v1.3. Technical report, ObjectManagement Group, 2000. UML Semantics.
[OMG01] OMG. Unified modeling language v1.4. Technical report, ObjectManagement Group, 2001. chap. 6 - Object Constraint LanguageSpecification.
[OMG03a] OMG. Uml 2.0 ocl specification. Technical report, Object Ma-nagement Group, 2003. ptc/03-10-14.
[OMG03b] OMG. Uml 2.0 superstructure specification. Technical report,Object Management Group, 2003. ptc/03-08-02.
[RBE+97] James Rumbaugh, Michel Blaha, Frederick Eddy, William Pre-merlani, and W. Lorensen. OMT, Modélisation et conceptionorientées objet. MASSON, 1997. Edition française.
24