28
_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes 1 Claude Belleil Université de Nantes Le langage UML 2.0 OCL (Object Constraint Language) 1 Préambule Ce document présente et définit les caractéristiques principales du Langage OCL (Object Constraint Language), en s’inspirant du document officiel de l’OMG 1 . OCL est un langage formel utilisé pour exprimer des contraintes dans un modèle objet. Le langage OCL a été utilisé dans le document de présentation de la sémantique du langage UML « UML semantics » 2 pour spécifier les règles de bonne formation du métamodèle UML. Chaque règle de bonne formation dans les sections concernant les modèles statiques des documents de spécification UML contient une expression OCL qui est un invariant de la classe concernée. La grammaire OCL est précisée à la fin de ce document. Un analyseur syntaxique 3 (OCL parser) construit à partir de cette grammaire a correctement traité toutes les contraintes correspondant au document « UML semantics ». C’est un processus qui a contribué à améliorer l’exactitude des spécifications pour OCL et UML. 1.1 Pourquoi OCL? En modélisation orientée objet, un modèle graphique comme la représentation d’une classe n’est parfois pas suffisant à exprimer la réalité, dans le cadre d’une spécification précise et non ambiguë. Il est souvent nécessaire de décrire des contraintes supplémentaires sur les objets du modèle. De telles contraintes sont souvent rédigées en langage naturel. La pratique a montré que cela aboutissait souvent à des ambiguïtés. Afin d’écrire des contraintes sans ambiguïtés, des langages également appelés « langages formels » ont été développés. De tels langages présentent un inconvénient majeur. Ils ne peuvent être utilisés que par des personnes ayant une bonne pratique de la notation mathématique, ce qui constitue souvent un handicap dans les domaines de la gestion ou de la modélisation des systèmes. OCL a été développé pour palier cette difficulté. C’est un langage formel qui reste facile à lire et à écrire. Il a été développé comme un langage de modélisation de gestion dans le département IBM de l’assurance et prend ses racines dans la méthode Syntropy 4 . 1 http://www.omg.org/ 2 http://www-306.ibm.com/software/rational/uml/resources/documentation.html 3 http://www-306.ibm.com/software/awdtools/library/standards/ocl-download.html 4 http://www.sciences.univ-nantes.fr/info/perso/permanents/habrias/lexique.html

Le Langage Oclmfworld42.free.fr/.../10_Langage%20OCL-Pr%E9sentation/01_lelangag… · Le Langage UML 2.0 Claude Belleil Université de Nantes 2 OCL est un langage d’expressions

Embed Size (px)

Citation preview

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

1

Claude Belleil Université de Nantes

Le langage UML 2.0 OCL (Object Constraint Language)

1 Préambule Ce document présente et définit les caractéristiques principales du Langage OCL (Object Constraint Language), en s’inspirant du document officiel de l’OMG1. OCL est un langage formel utilisé pour exprimer des contraintes dans un modèle objet. Le langage OCL a été utilisé dans le document de présentation de la sémantique du langage UML « UML semantics »2 pour spécifier les règles de bonne formation du métamodèle UML. Chaque règle de bonne formation dans les sections concernant les modèles statiques des documents de spécification UML contient une expression OCL qui est un invariant de la classe concernée. La grammaire OCL est précisée à la fin de ce document. Un analyseur syntaxique3 (OCL parser) construit à partir de cette grammaire a correctement traité toutes les contraintes correspondant au document « UML semantics ». C’est un processus qui a contribué à améliorer l’exactitude des spécifications pour OCL et UML.

1.1 Pourquoi OCL? En modélisation orientée objet, un modèle graphique comme la représentation d’une classe n’est parfois pas suffisant à exprimer la réalité, dans le cadre d’une spécification précise et non ambiguë. Il est souvent nécessaire de décrire des contraintes supplémentaires sur les objets du modèle. De telles contraintes sont souvent rédigées en langage naturel. La pratique a montré que cela aboutissait souvent à des ambiguïtés. Afin d’écrire des contraintes sans ambiguïtés, des langages également appelés « langages formels » ont été développés. De tels langages présentent un inconvénient majeur. Ils ne peuvent être utilisés que par des personnes ayant une bonne pratique de la notation mathématique, ce qui constitue souvent un handicap dans les domaines de la gestion ou de la modélisation des systèmes. OCL a été développé pour palier cette difficulté. C’est un langage formel qui reste facile à lire et à écrire. Il a été développé comme un langage de modélisation de gestion dans le département IBM de l’assurance et prend ses racines dans la méthode Syntropy4. 1 http://www.omg.org/ 2 http://www-306.ibm.com/software/rational/uml/resources/documentation.html 3 http://www-306.ibm.com/software/awdtools/library/standards/ocl-download.html 4 http://www.sciences.univ-nantes.fr/info/perso/permanents/habrias/lexique.html

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

2

OCL est un langage d’expressions. De plus, une expression OCL est garantie comme étant sans effet de bord5. Elle ne peut pas provoquer de changements dans le modèle. Cela signifie que l’état du système ne sera jamais modifié du fait de l’évaluation d’une expression OCL. En revanche, une expression OCL peut être utilisée pour spécifier un changement d’état, par exemple dans une post-condition comme nous le verrons par la suite. Les valeurs pour tous les objets, incluant également tous les liens ne seront pas pour autant changées. A chaque fois qu’une expression OCL est évaluée, elle délivre simplement une valeur. OCL n’est pas un langage de programmation. Ainsi, il n’est pas possible d’écrire une logique de programme ou un contrôle de flux en OCL. On ne peut pas appeler des processus ou activer des opérations au sens algorithmique. Parce qu’OCL est avant tout un langage de modélisation, il ne contient pas d’instructions qui soient directement exécutables. Par ailleurs, OCL est un langage typé. Chaque expression OCL possède un type. Par conséquent, dans une expression OCL correcte, les types utilisés doivent être conformes (on ne peut pas comparer un entier avec une chaîne de caractères). En OCL, les types sont ceux répertoriés dans le langage UML. En tant que langage de modélisation, toutes les questions d’implémentation sont en dehors du champ d’application et ne peuvent pas être exprimées dans ce langage. Chaque expression OCL est conceptuellement élémentaire. L’état des objets du système ne peut pas être changé pendant son évaluation.

1.2 Comment utiliser OCL OCL peut être utilisé avec des objectifs différents:

• Pour spécifier des invariants sur des classes ou des types dans un modèle de classes. • Pour spécifier un type invariant pour un stéréotype. • Pour décrire des pré et post conditions sur des opérations et des méthodes. • Pour décrire des gardes • Comme langage de navigation dans un diagramme • Pour spécifier des contraintes sur des opérations.

Dans le document consacré à la sémantique d’UML, OCL est utilisé dans les règles de bonne formation en tant qu’invariant sur les méta-classes. A plusieurs endroits, le langage est également utilisé pour définir des opérations additionnelles utilisées dans les règles de bonne formation. Depuis la version 1.4 d’UML, ces opérations additionnelles peuvent être formellement définies en utilisant les contraintes de définition et les expressions d’affectation.

2 Introduction

2.1 Légende Le texte écrit en police de caractère courier tel que ci-dessous est une expression OCL:

5 Modification indirecte de la valeur d'une variable. C'est une traduction mot à mot de l'expression anglaise « side effect » qui signifie en bon français « effet secondaire ». C'est ce qui se passe « au bord » d'un programme, et qui n'arriverait pas si le programme s'exécutait tout seul dans le vide. Mais le programme s'exécute au milieu d'un système dans lequel, en général, d'autres applications sont en train de s'exécuter

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

3

'This is an OCL expression' Le mot réservé context introduit le contexte d’une expression. Les mots réservés inv, pre et post signalent respectivement les stéréotypes invariant, précondition et postcondition de la contrainte. L’expression OCL suivante commence après les deux points : context TypeName inv: 'this is an OCL expression with stereotype <<invariant>> in the context of TypeName' = 'another string' Dans les expressions OCL de ce document, les mots réservés sont écrits en caractères gras. Les caractères gras n’ont pas de signification formelle mais sont utilisés pour rendre les expressions plus lisibles. Les expressions OCL n’utilisent que les caractères ASCII.

2.2 Diagramme de classes

Figure 1 : Diagramme de classe "exemple"

Le diagramme de classes ci-dessous est utilisé pour les exemples.

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

4

3 Lien avec le méta modèle UML

3.1 Self Chaque expression OCL est écrite dans le contexte d’une instance d’un type spécifique. Dans une expression OCL, le nom self est utilisé pour faire référence à l’instance de contexte. Par exemple, si le contexte est Company, alors self fait référence à une instance de Company6.

3.2 Spécification du contexte UML Dans un modèle UML, le contexte d’une expression OCL peut être spécifié au travers d’une déclaration de contexte au début d’une expression OCL. Si la contrainte est indiquée dans un diagramme avec son propre stéréotype et les lignes de pointillés la reliant à son élément de contexte, il n’est pas nécessaire d’avoir une déclaration explicite de contexte dans le test de la contrainte. La déclaration de contexte est optionnelle.

3.3 Les invariants Une expression OCL peut être un élément d’un invariant qui est une contrainte stéréotypée avec «invariant». Quand l’invariant est associé à un classificateur, celui-ci est référencé en tant que « type ». L’expression est alors un invariant du type et doit être vrai pour les instances ce type à n’importe quel moment. Par exemple, dans le contexte du type Company, l’expression suivante spécifie comme un invariant que le nombre d’employés doit toujours être supérieur à 50 self.numberOfEmployees > 50 Ici, self est une instance du type Company. On peut voir self comme un objet à partir duquel on construit l’expression. Le type de l’instance de contexte d’une expression OCL qui est un élément d’un invariant est écrit avec le mot réservé context suivi par le nom du type. L’étiquette inv : declare la contrainte comme étant une contrainte invariante : context Company inv:

self.numberOfEmployees > 50 Dans de nombreux cas, self peut être omis, parce que le contexte est clair comme dans les exemples ci-dessous. Comme alternative à l’utilisation de self, un nom différent peut être défini, jouant ainsi le rôle de self. context c:Company inv:

c.numberOfEmployees > 50 Cet exemple est identique à l’exemple précédent qui utilisait self. De façon optionnelle, le nom de la contrainte peut être écrit après le mot réservé inv permettant à la contrainte d’être référencée par un nom. Dans l’exemple suivant le nom de la contrainte est

6 Donc à un objet

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

5

enoughEmployees. Dans le méta modèle UML, ce nom est un attribut de la métaclasse Constraint qui est héritée de ModelElement. context c : Company inv enoughEmployees:

c.numberOfEmployees > 50

3.4 Pré et post conditions Une expression OCL peut être un élément d’une pré-condition ou d’une post-condition correspondant aux stéréotypes de contraintes associés à une opération ou à une méthode. Alors, l’instance de contexte self est une instance du type qui possède l’opération ou la méthode comme caractéristique. En OCL, la déclaration de contexte utilise le mot clé context, suivi du type et de la déclaration d’opération. Les étiquettes pre : et post : déclarent les contraintes comme étant respectivement une contrainte de pré condition ou une contrainte de post condition. context Typename::operationName(param1 : Type1, ... ): ReturnType

pre : param1 > ... post: result = ...

Le nom self peut être utilisé dans l’expression faisant référence à l’objet sur lequel l’opération a été appelée, et le nom du résultat est le nom de l’objet retourné, s’il y en a un. Les noms des paramètres (param1,… ) peuvent aussi être utilisés dans une expression OCL. Dans le diagramme de classes de l’exemple, nous pouvons écrire : context Person::income(d : Date) : Integer

post: result = 5000

De façon optionnelle, le nom d’une pré ou post condition peut être écrit après les mots réservés pre ou post, permettant à la contrainte d’être référencée par son nom. Dans l’exemple suivant, le nom de la pré condition est parameterOk et le nom de la post condition resultOk. Dans le méta modèle UML, ces noms sont des attributs de la métaclasse Constraint qui hérite de ModelElement context Typename::operationName(param1 : Type1, ... ): ReturnType

pre parameterOk: param1 > ... post resultOk: result = ...

3.5 Le contexte Package La déclaration de contexte package est assez précise quand le package dans lequel le classificateur apparaît est évident dans l’environnement. Pour spécifier explicitement dans quel package les contraintes invariant, pré ou post apparaissent, celles-ci peuvent être encadrées par les instructions package et endpackage. Les instructions de package ont la syntaxe suivante : package Package::SubPackage context X inv:

... some invariant ... context X::operationName(..)

pre: ... some precondition ...

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

6

endpackage Un fichier ou un flux OCL peuvent contenir n’importe quel nombre d’instructions de package, permettant ainsi à tous les invariants, pré conditions et post conditions d’être écrits à la suite les uns des autres et rangés dans un fichier. Ce fichier peut coexister avec un modèle UML en tant qu’entité séparée.

3.6 Expressions générales Toute expression OCL peut être utilisée comme valeur pour un attribut de la classe UML Expression ou pour un de ses sous types. Dans ce cas, les documents sémantiques décrivent la signification de l’expression.

4 Valeurs élémentaires et types En OCL, le nombre de types élémentaires est prédéfini et disponible à tout moment pour le modélisateur. Ces types de valeurs prédéfinis sont indépendants de tout modèle objet et font partie de la définition d’OCL. La valeur la plus élémentaire en OCL est la valeur d’un des types de base. Les quelques types élémentaires utilisés dans les exemples de ce document sont les suivants :

type valeurs Boolean true, false Integer 1, 2, 34, 26524, ... Real 1.5, 3.14, ... String 'To be or not to be...'

OCL définit un certain nombre d’opérations sur les types prédéfinis. Le tableau suivant donne quelques exemples d’opérations et de types prédéfinis. La liste complète des opérations est donnée en section 6.8 du document7 de l’OMG (Object Constraint Language Specification)

type operations Integer *, +, -, /, abs() Real *, +, -, /, floor() Boolean and, or, xor, not, implies, if-then-else String toUpper(), concat()

Collection, Set, Bag et Sequence sont également des types élémentaires. Leurs spécificités seront décrites dans les sections suivantes.

4.1 Types provenant du modèle UML Chaque expression OCL est écrite dans le contexte d’un modèle UML, d’un certain nombre de types/classes de leurs caractéristiques et associations et de leurs généralisations. Tous les types/classes provenant d’un modèle UML sont typés en OCL.

4.2 Types énumération Les énumérations correspondent à des types de données UML. Elles possèdent un nom, comme n’importe quel autre classificateur.

7 Voir le document : OCL_Annexe.pdf

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

7

Une énumération définit un ensemble de littéraux qui correspondent aux valeurs possibles de l’énumération. Dans le cadre du langage OCL, on peut faire référence à la valeur d’une énumération. Quand on a un type de données nommé Sex avec les valeurs « female » ou « male », ils peuvent être utilisés de la façon suivante : context Person inv: sex = Sex::male

4.3 L’expression Let et les contraintes de définition Parfois une sous-expression est utilisée plusieurs fois dans une contrainte. L’expression let permet de définir un attribut ou une opération qui peut être utilisé dans une contrainte. context Person inv: let income : Integer = self.job.salary->sum() let hasTitle(t : String) : Boolean =

self.job->exists(title = t) in if isUnemployed then

self.income < 100 else

self.income >= 100 and self.hasTitle(`manager') endif Une expression let peut être incluse dans un invariant, une pré ou post condition. Alors, elle est uniquement connue à l’intérieur de cette contrainte spécifique. Pour pouvoir réutiliser les opérations et/ou variables let, on peut utiliser une contrainte avec le stéréotype définition def: dans laquelle les opérations et/ou variables let sont définies. Cette définition de contrainte doit être attachée à un classificateur et ne peut contenir que les définitions let. Toutes les variables et opérations définies dans la contrainte de définition def : sont connues dans le même contexte où les propriétés du classificateur peuvent être utilisées. Par essence, de telles variables et/ou opérations sont des pseudo attributs ou des pseudo opérations du classificateur. Elles sont utilisées dans une expression OCL exactement de la même façon qu’un attribut ou une opération. La contrainte de définition utilise le mot réservé def : comme il est montré ci-dessous : context Person def:

let income : Integer = self.job.salary->sum() let hasTitle(t : String) : Boolean = self.job->exists(title = t)

Les noms des attributs et des opérations dans une expression let ne doivent pas entrer en conflit avec des noms d’attributs de terminaisons d’association (rôle) ou d’opérations déjà définis dans le classificateur. De plus, les noms de toutes les variables et opérations en relation avec le classificateur doivent être uniques.

4.4 Conformité de types OCL est un langage typé et les valeurs élémentaires de types sont organisées dans une hiérarchie de types. Cette hiérarchie détermine la conformité des différents types les uns par rapport aux autres. Par exemple on ne peut pas comparer un Integer avec un Boolean ou un String. Une expression OCL dans laquelle tous les types sont en conformité est une expression valide. Une expression OCL dans laquelle les types ne sont pas en conformité est une expression invalide. Elle contient une erreur de conformité de type. Un type type1 est en conformité avec un type type2 quand une instance de type1 peut être substituée à chaque endroit où une instance de type2 est attendue. Dans les diagrammes de classes, les règles de conformité de type sont simples :

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

8

• Chaque type et conforme à son supertype • La conformité de type est transitive. Si type1 est conforme à type2 et type2 conforme à type3,

alors type1 est conforme à type3 La conséquence de cette règle est qu’un type est conforme à son supertype et à tous les supertype au dessus. Les règles de conformité de type pour les valeurs de type sont les suivantes: Type conforme à.. / est un sous type de.. Set Collection Sequence Collection Bag Collection Integer Real La relation de conformité entre les types Collection est seulement conservée si il y a des collections de types qui sont conformes l’un à l’autre. Voir le paragraphe 5.13 pour les règles complètes de conformité pour les collections. Dans la table suivante quelques exemples d’expressions valides et non valides sont présentées : OCL expression valid? error 1 + 2 * 34 yes 1 + 'motorcycle' no type Integer does not conform to type String 23 * false no type Integer does not conform to Boolean 12 + 13.5 yes

4.5 Re typage ou consolidation Dans certaines circonstances, il est souhaitable d’utiliser une propriété d’un objet qui est définie sur un sous type du type courant connu de l’objet. Parce que la propriété n’est pas définie sur le type courant connu, ceci a pour résultat une erreur de conformité de type. Quand il est certain que le type actuel de l’objet est un sous type, l’objet peut être retypé en utilisant l’opération oclAsType(OclType). Cette opération a pour résultat le même objet, mais le type connu est l’argument OclType. Quand il y a un objet object de type Type1 et que Type2 est un autre type, il est permis d’écrire: object.oclAsType(Type2) --- évalue l’objet avec le type Type2 Un objet peut seulement être retypé dans un de ses sous-types, par conséquent, dans l’exemple précédent Type2 doit être un sous type de Type1. Si le type actuel de l’objet n’est pas égal au type pour lequel il est retypé, l’expression est indéfinie (voir 4.10)

4.6 Règles de précédence L’ordre de priorité pour les opérations en OCL est le suivant:

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

9

. @pre . dot and arrow operations: `.' and `->' . unary `not' and unary minus `-' . `*' and `/' . `+' and binary `-' . `if-then-else-endif' . `<', `>', `<=', `>=' . `=', `<>' . `and', `or' and `xor' . `implies'

Des parenthèses peuvent être utilisées pour changer l’ordre des priorités.

4.7 Utilisation des opérateurs infixes8 L’utilisation des opérateurs infixes est autorisée en OCL. Les opérateurs suivants sont utilisés comme opérateurs infixes: ‘+’, ‘-', ‘*', ‘/', ‘<‘, ‘>', ‘<>' ‘<=' ‘>=', ‘and', ‘or', et ‘xor' L’expression : a + b est conceptuellement équivalente à l’expression: a.+(b) qui appelle l’opération + sur a avec b comme paramètre de l’opération. Les opérateurs infixes définis pour un type doivent avoir exactement un paramètre. Pour les opérateur infixes suivants : ‘<‘, ‘>', ‘<>' ‘<=' ‘>=', ‘and', ‘or', et ‘xor' Le type retourné doit être boolean.

4.8 Les commentaires En OCL, les commentaires sont placés après deux tirets. Tout ce qui est placé après deux tirets jusqu’à et y compris la fin de ligne est considéré comme un commentaire. Par exemple: -- ceci est un commentaire

8 Les expressions arithmétiques sont habituellement écrites de manière infixe, c'est à dire l'opérateur entre ses deux opérandes (ou avant si c'est un opérateur unaire mais c'est pas le problème). On trouve aussi une notation postfixée, que l'on appelle également notation polonaise car introduite par le polonais Lukasiewicz : l'opérateur est placé après ses opérandes.

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

10

4.9 Valeurs indéfinies A chaque fois qu’une expression OCL est évaluée, il existe une possibilité qu’une ou plusieurs requêtes dans l’expression soient indéfinies. Si c’est le cas, alors l’expression complète sera indéfinie. Il existe deux exceptions à cela concernant les opérateurs booléens :

• True OR n’importe quoi est égal à True • False AND n’importe quoi est égal à False

Les deux règles ci-dessus sont valides quelque soit l’ordre des arguments et si la valeur des autres sous expressions est connue ou non.

5 Objets et propriétés Les expressions OCL peuvent se référer à des types, des classes, des interfaces, des associations (agissant comme des types) et des types de données. Egalement, tous les attributs, terminaisons d’associations (rôles), méthodes et opérations sans effets de bord qui sont définis sur ces types, classes, … etc peuvent être utilisés. Dans un modèle de classes, une opération ou une méthode est définie sans effet de bord si l’attribut isQuery de l’opération est vrai. Dans la suite de ce document, nous ferons référence aux attributs, terminaisons d’associations, méthodes et opérations sans effet de bord comme étant des propriétés. Par conséquent, une propriété est un des éléments suivants :

• Un Attribut • Une terminaison d’association (rôle) • Une Association avec isQuery égal à vrai • Une Méthode avec isQuery égal à vrai

5.1 Les propriétés La valeur d’une propriété sur un objet qui est définie dans un diagramme de classe est spécifiée par un point, suivi par le nom de la propriété: context AType inv: self.property Si self est une référence à un objet, alors self.property est la valeur de la propriété property sur self.

5.2 Propriétés: attributs Par exemple, l’age d’une personne Person est noté de la façon suivante: context Person inv: self.age > 0 La valeur de cette expression est la valeur de l’attribut age sur la personne self.Le type de cette expression est le type de l’attribut age, qui est du type élémentaire Integer.

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

11

Avec les attributs et les opérations définis sur les valeurs de type élémentaire, nous pouvons exprimer des calculs sur le modèle de classe. Par exemple, une règle de gestion peut être « l’âge d’une personne est toujours supérieur ou égal à zéro ». Ceci peut être déclaré comme un invariant.

5.3 Propriétés: Opérations Les opérations peuvent avoir des paramètres. Par exemple, dans le diagramme de classes présenté plus haut, un objet Person a un revenu exprimé comme une fonction de date. Cette opération peut-être représentée comme ci-dessous pour une Personne aPersonn et une date aDate: aPerson.income(aDate) L’opération peut elle-même être définie par une contrainte de postcondition. C’est une contrainte qui est stéréotypée comme postcondition. L’objet qui est retourné par l’opération peut être référencé par result. La forme est la suivante : context Person::income (d: Date) : Integer post: result = age * 10009 La partie droite de cette définition peut faire référence à l’opération qui a été définie, c'est-à-dire que la définition peut être récursive, aussi longtemps que la récursion est correctement définie. Le type du résultat est le type retourné par l’opération, c'est-à-dire un entier dans l’exemple ci-dessus. Pour faire référence à une opération ou une méthode qui ne prennent pas de paramètres, des parenthèses avec une liste vide d’argument sont utilisés : context Company inv: self.stockPrice() > 0

5.4 Propriétés: Extrémités (rôles) d’associations et navigation En partant d’un objet spécifique, dans un diagramme de classes on peut parcourir une association pour faire référence aux autres objets et à leurs propriétés. Pour faire cela, on parcourt l’association en utilisant la partie terminale de l’association du côté opposé : object.rolename La valeur de cette expression est constituée de l’ensemble des objets de l’autre côté du nom de rôle de l’association. Si la cardinalité de la terminaison de l’association a un maximum égal à 1 (0..1 ou 1), alors la valeur de cette expression est un objet. Dans le diagramme de classes de l’exemple, quand nous commençons la navigation dans le contexte de Company, c'est-à-dire, une instance de Company, on peut écrire : context Company inv: self.manager.isUnemployed = false inv: self.employee->notEmpty() Dans le premier invariant, self.manager est une personne, parce que la cardinalité de l’association est 1. L’évaluation de la seconde expression donnera un résultat de type ensemble de personnes. Par défaut, la navigation fournit un résultat du type Set.

9 Exemple bizarre, repris du document officiel !

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

12

Les collections tels que Set, Bag et Sequence sont des types prédéfinis en OCL. Il existe un grand nombre d’opérations prédéfinies les concernant. Une propriété de la collection peut être adressée en utilisant une flèche ‘->’ suivie par le nom de la propriété. L’exemple suivant se place dans le contexte d’une personne : context Person inv: self.employer->size() < 3 Ceci applique la propriété size (dimension) sur l’ensemble (Set) self.employer qui fournit comme résultat le nombre d’employés de Person self. context Person inv: self.employer->isEmpty() Ceci applique la propriété isEmpty sur l’ensemble self.employer. Cela est évalué à Vrai si l’ensemble des employés est vide et à faux dans l’autre cas.

5.4.1 Absence de Nom de rôle A chaque fois qu’un nom de rôle est omis sur une des extrémités d’une association, le nom du type de l’extrémité de l’association, commençant par une lettre minuscule est utilisé en tant que nom de rôle. Si le résultat est ambigu, le nom de rôle est obligatoire. C’est le cas avec les noms de rôle absents sur les associations réflexives. Si le nom de rôle est ambigu, il ne peut pas être utilisé en OCL.

5.4.2 Navigation sur des associations avec des cardinalités Zéro ou un Parce que la cardinalité du rôle manager est égale à un, self.manager est un objet de type Person. Un tel objet unique peut être utilisé comme un ensemble. Alors, il se comporte comme un ensemble ne contenant qu’un seul objet. L’usage en tant que set est fait au travers de la flèche, suivi de la propriété de l’ensemble. Ceci est montré dans l’exemple suivant : context Company inv: self.manager->size() = 1 self.manager est utilisé en tant qu’ensemble (Set) parce que la flèche est utilisée pour accéder à la propriété size sur l’ensemble. Le résultat renvoyé est 1 L’exemple suivant illustre comment une propriété de collection peut être utilisée. context Company inv: self.manager->foo10 self.manager est utilisé en tant qu’ensemble (Set) parce que la flèche est utilisée pour accéder à la propriété foo sur l’ensemble. Cette expression est incorrecte puisque foo n’est pas une propriété définie de l’ensemble. context Company inv: self.manager.age> 40 self.manager est utilisé en tant que Personne parce que le point est utilisé pour accéder à la propriété age de Person 10 Jargon américain attribuant un nom à un programme et signifiant « machin », « truc », souvent associé à bar

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

13

Dans le cas d’une cardinalité optionnelle (0..1), ceci est spécialement utilisé pour contrôler si il y a un objet ou non quand on parcourt l’association. Dans cet exemple nous pouvons écrire : context Person inv: self.wife->notEmpty() implies self.wife.sex = Sex::female

5.4.3 Propriétés combinées Les propriétés peuvent être combinées pour faire des expressions plus complexes. Une importante règle est qu’une expression OCL est toujours évaluée pour un objet d’un type spécifique. Sur ce résultat, on peut toujours appliquer une autre propriété. Ainsi, chaque expression OCL peut être lue et évaluée de la gauche vers la droite. Voici quelques invariants qui utilisent des propriétés combinées à partir de l’exemple du diagramme de classe du début de ce document. Les gens mariés sont agés de plus de 18 ans context Person inv: self.wife->notEmpty() implies self.wife.age >= 18 and self.husband->notEmpty() implies self.husband.age >= 18 Une entreprise a au moins 50 employés context Company inv: self.employee->size() >= 50

5.5 Navigation et types d’associations Pour spécifier la navigation sur les classes associations (Job et marriage dans l’exemple), OCL utilise un point (.) et le nom de la classe association qui commence par une minuscule : context Person inv: self.job La sous expression self.job est évaluée comme l’ensemble de tous les emplois qu’une personne a avec les entreprises qui sont son employeur. Dans le cas d’une classe association, il n’y a pas de nom de rôle explicite dans le diagramme de classes. Le nom job utilisé dans cette navigation est le nom de la classe association commençant par une minuscule, de façon similaire à celle décrite précédemment dans la section «Absence de Nom de rôle ».

Figure 2 : navigation récursive sur une classe association

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

14

Quand on parcourt un modèle sur une classe association telle que EmployeeRanking11 , il y a deux possibilités qui dépendent de la direction du parcours. Par exemple, dans l’exemple ci-dessus on peut parcourir vers le rôle employees ou vers le rôle bosses. En n’utilisant que le nom de la classe association, ces deux options ne peuvent pas être différenciées. Pour réaliser cette distinction, le nom du rôle vers lequel on veut effectuer le parcours doit être ajouté au nom de la classe association en le plaçant entre crochets. Dans l’expression : context Person inv: self.employeeRanking[bosses]->sum() > 0 self.employeeRanking[bosses] évalue l’ensemble employeeRanking appartenant à la collection bosses. Et dans l’expression : context Person inv: self.employeeRanking[employees]->sum() > 0 self.employeeRanking[employees] évalue l’ensemble employeeRanking appartenant à la collection employees.

5.6 Navigation à partir des classes associations On peut naviguer à partir d’une classe association vers les objets qui participent à cette association. Ceci est réalisé en utilisant la notation par le point et les noms de rôles sur les extrémités des associations. context Job inv: self.employer.numberOfEmployees >= 1 inv: self.employee.age > 21 La navigation à partir d’une classe association vers un des objets de l’association délivrera toujours exactement un objet. C’est le résultat de la définition de la classe association. Par conséquent, le résultat de cette navigation est exactement un objet, bien qu’il puisse être utilisé comme un ensemble utilisant la flèche (->).

5.7 Navigation au travers des associations qualifiées Les associations qualifiées utilisent un ou plusieurs attributs qualifiés pour sélectionner les objets à l’extrémité opposée de l’association. Pour les parcourir, on peut ajouter les valeurs aux qualificateurs pour la navigation. Ceci est fait en utilisant des crochets, à la suite du nom de rôle. Il est autorisé de sauter par dessus les valeurs du qualificateur, dans ce cas le résultat sera constitué de tous les objets à l’autre extrémité de l’association. context Bank inv: self.customer Fournit comme résultat un ensemble Set(Person) contenant tous les clients de la banque self.customer[8764423]

11 Hiérarchie

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

15

Fournit comme résultat une personne ayant le compte bancaire n°8764423 S’il y a plus d’un attribut qualifié, les valeurs sont séparées par des virgules. Il n’est pas possible de spécifier partiellement des valeurs d’attributs.

5.8 Utilisation des noms de chemins pour les paquetages et les propriétés.

En UML, différents types sont organisés en paquetage. OCL fournit une méthode pour faire référence de façon explicite aux types des autres paquetages en utilisant les noms de chemins comme préfixe des noms de paquetages. La syntaxe est la suivante : Packagename::Typename Cette utilisation des noms de chemins est transitive et peut être mise en oeuvre pour des paquetages inclus dans d’autres paquetages : Packagename1::Packagename2::Typename

5.9 Accès aux propriétés des super types A chaque fois que des propriétés sont redéfinies dans un type, la propriété des super types peut être atteinte en utilisant la même syntaxe des chemins. Quand on a une classe B comme un sous type de la classe A et une propriété p1 à la fois de A et de B, on peut écrire. context B inv: self.oclAsType(A).p1 -- accès à la propriété p1 définie dans A self.p1 -- accès à la propriété p1 définie dans B L’exemple suivant illustre la nécessité de l’utilisation des noms de chemins.

Figure 3 : Ambiguïté de parcours

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

16

Dans ce fragment de modèle, il existe une ambiguïté avec l’expression OCL sur Dependency: context Dependency inv: self.source <> self Celle-ci peut avoir pour signification une association normale de navigation qui est héritée de ModelElement. Mais elle peut également signifier une navigation au travers du lien en pointillés en tant que classe association. Les deux parcours possibles utilisent le même nom de rôle. Aainsi, l’ambiguïté subsiste. En utilisant oclAsType() nous pouvons les distinguer l’une de l’autre : context Dependency inv: self.oclAsType(Dependency).source inv: self.oclAsType(ModelElement).source

5.10 Caractéristiques prédéfinies sur tous les objets Il existe plusieurs caractéristiques qui s’appliquent à l’ensemble des objets et qui sont prédéfinis en OCL: oclIsTypeOf(t : OclType) : Boolean oclIsKindOf(t : OclType) : Boolean oclInState(s : OclState) : Boolean oclIsNew() : Boolean oclAsType(t : OclType) : instance of OclType L’opération oclAsType fournit un résultat à vrai si le type de self et t sont les mêmes. Par exemple : context Person inv: self.oclIsTypeOf( Person ) -- est vrai inv: self.oclIsTypeOf( Company) -- est faux L’expression ci-dessus permet d’accéder directement au type direct d’un objet. oclIsKindOf détermine si t est soit le type direct ou un des super types d’un objet. L’opération oclInState(s) fournit un résultat à vrai si l’objet est dans un état s. Les valeurs de s sont les noms des états dans le diagramme d’états-transitions lié au classificateur de l’objet. Pour emboîter les états, les noms des états peuvent être combinés en utilisant le signe « :: ».

Figure 4: Propriétés sur un automate à états

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

17

Dans l’exemple ci-dessus les valeurs de s peuvent être On, Off, Off ::Standby, Off ::NoPower. Si le classificateur de l’objet est associé au diagramme d’états-transitions ci-dessus, les expressions OCL suivantes sont valides: object.oclInState(On) object.oclInState(Off) object.oclInstate(Off::Standby) object.oclInState(Off:NoPower) S’il y a plusieurs diagrammes d’états attachés au classificateur de l’objet, alors le nom d’un l’état peut être préfixé par le nom du diagramme d’états contenant l’état et le double « :: » comme avec les états emboîtés.

5.11 Caractéristiques sur les classes elles-mêmes Toutes les propriétés présentées jusqu’à présent en OCL, sont des propriétés sur des instances de classes. Les types sont, soit définis en OCL, soit définis dans le modèle de classes. En OCL, il est également possible d’utiliser des caractéristiques définies sur les types/classes eux-mêmes. Ce sont, par exemple, les caractéristiques définies dans le modèle de classe. De plus, plusieurs caractéristiques sont prédéfinies sur chacun des types. La caractéristique prédéfinie la plus importante sur chaque type est allInstances, qui fournit en résultat l’ensemble (Set) de toutes les instances du type. Si nous voulons garantir que toutes les instances de Person ont des noms uniques nous pouvons écrire : context Person inv: Person.allInstances->forAll(p1, p2 | p1 <> p2 implies p1.name <> p2.name) Person.allInstances est l’ensemble de toutes les personnes et est du type Set(Person). Il représente l’ensemble de toutes les personnes qui existent au moment où l’expression est évaluée.

5.12 Collections Un parcours simple a le plus souvent pour résultat : un ensemble (Set), � des parcours combinés : un sac (Bag) � des parcours sur des associations ornées de rôles avec une contrainte {ordered} une séquence

(Sequence). Par conséquent, le type collection jouent un rôle important dans les expressions OCL. Le type collection est prédéfini en OCL. Il comprend un grand nombre d’opérations prédéfinies pour permettre à l’auteur des expressions OCL de manipuler ces collections. Avec la définition d’OCL en tant que langage d’expression, les opérations sur les collections de changent jamais les collections. isQuery est toujours vrai. Elles peuvent fournir un résultat sous la forme d’une collection, mais plutôt que de changer la collection d’origine, elles fournissent le résultat dans une nouvelle collection. Collection est un type abstrait avec des types de collections concrètes comme sous types. OCL distingue trois types différents types de collections : � l’ensemble (Set),

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

18

� la séquence (Sequence) � le sac (Bag).

Set correspond à l’ensemble de type mathématique. Il ne peut pas contenir d’éléments dupliqués. Set { 1 , 2 , 5 , 88 } Set { 'apple' , 'orange', 'strawberry' } Bag est comme un Set qui peut contenir des doublons, c'est-à-dire que le même élément peut être dans un Bag deux ou plusieurs fois. Bag {1 , 3 , 4, 3, 5, 1 } Une séquence (Sequence) est comme un sac (Bag) dans lequel les éléments sont ordonnés. Sequence {1 , 1, 3 , 3, 4, 5 } Les sacs (Bag) et les ensembles (Set) n’ont pas ordre défini. Set, Sequence et Bag peuvent être spécifiés par un littéral. Des petites accolades (curly brackets) encadrent les éléments de la collection qui sont séparés par des virgules. Le type de la collection est écrit avant les accolades. Du fait de l’utilisation fréquente de séquences consécutives de nombres entiers, il existe un littéral de séparation pour les créer. Les éléments entre les accolades peuvent être remplacés par une spécification d’intervalle qui consiste en deux expressions de type Integer, Int-expr1 et Int-expr2 séparées par « .. ». Ceci indique tous les Entiers compris entre les valeurs de Int-expr1 et Int-expr2, incluant les valeurs de Int-expr1 et Int-expr2 elles-mêmes : Sequence{ 1..(6 + 4) } Sequence{ 1..10 } Sont identiques à Sequence{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } La liste complète des opérations sur collection est décrite dans le document annexe. Les Collections peuvent être spécifiées par un littéral comme décrit ci-dessous. La seule autre façon d’obtenir une collection est la navigation. Pour être plus précis, la seule façon d’obtenir Set, Sequence ou Bag est :

1. Un littéral qui fournira un résultat sous la forme d’un ensemble, d’une séquence ou d’un sac : Set {1 , 2, 3 , 5 , 7 , 11, 13, 17 } Sequence {1 , 2, 3 , 5 , 7 , 11, 13, 17 } Bag {1, 2, 3, 2, 1}

2. Un parcours commençant à partir d’un objet peut fournir en résultat une collection: context Company inv: self.employee

3. Des opérations sur des collections peuvent fournir une nouvelle collection: collection1->union(collection2)

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

19

5.13 Collections de collections Dans le langage OCL, toutes les collections de collections sont automatiquement mises à plat. Ainsi, les deux expressions suivantes ont la même valeur : Set{ Set{1, 2}, Set{3, 4}, Set{5, 6} } Set{ 1, 2, 3, 4, 5, 6 }

5.14 Hiérarchie de type collection et règles de conformité de types En plus des règles de conformité de types vues au paragraphe 4.4, les règles suivantes sont valides pour tous les types incluant les types collection :

• Chaque type Collection(X) est un sous type de OclAny. Les types Set (X), Bag (X) et Sequence (X) sont tous des sous types de Collection (X).

Les règles de conformité de types sont les suivantes pour les types collection :

• Un Type1 est conforme à un Type2 quand ils sont identiques (règle standard pour tous les types)

• Un Type1 est conforme à un Type2 quand il est un sous type du Type2 (règle standard pour tous les types)

• Une Collection(Type1) est conforme à une Collection(Type2) quand le Type1 et le Type2 sont conformes.

• La conformité de type est transitive: si un Type1 est conforme à un Type2, et que le Type2 est conforme à un Type3, alors les Type1 et Types3 sont conformes (règle standard pour tous les types)

Par exemple si Bicycle et Car sont deux sous types distincts de Transport: Set(Bicycle) est conforme à Set(Transport) Set(Bicycle) est conforme à Collection(Bicycle) Set(Bicycle) est conforme à Collection(Transport) On notera que Set(Bicycle) n’est pas en conformité avec Bag(Bicycle) … Ce sont deux sous types de Collection (Bicycle) au même niveau dans la hiérarchie.

5.15 Valeurs préalables dans les Post Conditions Comme cela a été vu à la section 3.4, OCL peut être utilisé pour spécifier des Pré et Post conditions sur des opérations et des méthodes en UML. Dans une post condition, l’expression peut faire référence à deux ensembles de valeurs pour chaque propriété d’un objet :

• La valeur d’une propriété au début de l’opération ou de la méthode • La valeur d’une propriété après l’exécution de l’opération ou de la méthode

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

20

La valeur d’une propriété dans une post condition correspond aux valeurs après exécution de l’opération. Pour faire référence à la valeur d’une propriété au début de l’opération on post fixe le nom de la propriété avec le mot réservé « @pre » : context Person::birthdayHappens() post: age = age@pre + 1 La propriété age fait référence à la propriété de l’instance de Person sur laquelle s’exécute l’opération. La propriété age@pre fait reference à la valeur de la propriété age de Person qui execute l’opération, au debut de l’opération. Si la propriété a des paramètres, @pre est postfixée du nom de la propriété avant les paramètres : context Company::hireEmployee(p : Person) post: employees = employees@pre->including(p) and stockprice() = stockprice@pre() + 10 alors l’opération ci-dessus peut également être spécifiée conjointement par une pré et post condition context Company::hireEmployee(p : Person) pre : not employee->includes(p) post: employees->includes(p) and stockprice() = stockprice@pre() + 10 Quand la pré valeur de la propriété est prise et évaluée pour un objet, toutes les propriétés suivantes auxquelles on a accès sont des valeurs nouvelles de cet objet (jusqu’à l’exécution de l’opération) ainsi: [email protected] prend l’ancienne valeur de la propriété b de a, appelée x et ensuite la nouvelle valeur c de x [email protected]@pre prend l’ancienne valeur de la propriété b de a, appelée x, et ensuite l’ancienne valeur de c de x. La notation post fixée “@pre” n’est autorisée que dans les expressions OCL qui font partie d’une post condition. Ainsi, pour une propriété courante d’un objet qui a été détruit pendant l’exécution de l’opération, le résultat est indéfini. Egalement, faisant référence à la valeur précédente d’un objet qui a été créé pendant l’exécution de l’opération, le résultat est indéfini.

6 Les opérations sur les collections OCL définit plusieurs opérations sur les types Collection. Ces opérations ont spécifiquement la capacité de générer de façon souple et puissante de nouvelles collections à partir de celles qui existent. Les différentes constructions sont décrites dans les paragraphes suivants.

6.1 Les opérations Select et Reject Parfois une expression utilisant des opérations et des navigations fournit comme résultat une collection alors que nous ne nous intéressons qu’à un sous ensemble de cette collection. OCL dispose d’opérateurs

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

21

spéciaux pour réaliser une sélection à partir d’une collection spécifique. La sélection spécifie un sous ensemble de la collection. Une sélection est une opération sur une collection et est réalisée en utilisant la flèche : collection->select( ... ) Le paramètre de la sélection possède une syntaxe spéciale qui permet à la sélection de spécifier quels éléments de la collection nous souhaitons sélectionner. Il existe trois formes différentes, parmi lesquelles la plus simple est la suivante : collection->select( boolean-expression ) Cela fournit comme résultat une collection qui contient tous les éléments de la collection pour lesquels l’expression booléenne a été évaluée à Vrai. Pour trouver le résultat de cette expression, l’expression boolean-expression est évaluée pour chaque élément de la collection. Si l’évaluation est Vraie, l’élément est inclus dans la « collection résultat », sinon, il ne l’est pas. A titre d’exemple, l’expression OCL suivante spécifie que la collection de tous les employés ayant plus de 50 ans n’est pas vide. context Company inv: self.employee->select(age > 50)->notEmpty() self.employee est du type Set(Person). La sélection prend une personne à partir de self.employee puis évalue age > 50 pour cette personne. Si le résultat est vrai, alors la personne est mise dans l’ensemble résultat. Comme il a été montré dans l’exemple précédent, le contexte pour l’expression dans l’argument du select est l’élément de la collection sur lequel le select est invoqué. Ainsi, la propriété age est prise dans le contexte d’une personne. Dans l’exemple ci-dessus, il est impossible de faire explicitement référence aux personnes elles-mêmes. On peut seulement faire référence à leurs propriétés. Afin de pouvoir faire référence aux personnes elles-mêmes, il existe une syntaxe plus générale pour l’expression de sélection : Collection->select( v | boolean-expression-with-v ) La variable v est appelée l’itérateur. Quand la selection est évaluée, v réalise des itérations sur la collection et boolean-expression-with-v est évalué pour chaque valeur de v. La variable v est une référence à l’objet à partir de la collection et peut être utilisée pour faire référence aux objets eux-mêmes à partir de la collection. Les deux exemples ci-dessous sont identiques : context Company inv: self.employee->select(age > 50)->notEmpty() context Company inv: self.employee->select(p | p.age > 50)->notEmpty() Le résultat d’une sélection complète est une collection de personnes pour lesquelles l’expression p.age > 50 est évaluée à Vrai. C’est un sous ensemble de self.employee. Comme extension finale pour la syntaxe du select, le type attendu de la variable v peut être donné. Le select s’écrit alors : Collection->select( v : Type | boolean-expression-with-v )

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

22

La signification de cela est que les objets de la collection doivent être du type Type. L’exemple suivant est identique aux précédents : context Company inv: self.employee.select(p : Person | p.age > 50)->notEmpty() La syntaxe complète du select ressemble à l’une des formes suivantes: collection->select( v : Type | boolean-expression-with-v ) collection->select( v | boolean-expression-with-v ) collection->select( boolean-expression ) L’opération reject est identique à l’opération select mais avec reject on obtient le sous ensemble de tous les éléments de la collection pour lesquels l’expression est évaluée à Faux. La syntax est identique à celle du select : Collection->reject( v : Type | boolean-expression-with-v ) Collection->reject( v | boolean-expression-with-v ) Collection->reject( boolean-expression ) A titre d’exemple, spécifions que la collection de tous les employés qui ne sont pas mariés est vide: context Company inv: self.employee->reject( isMarried )->isEmpty() L’opération de rejet est disponible en OCL pour des raisons de commodité car chaque rejet peut être réécrit comme une sélection avec une expression de négation. Ainsi, les deux expressions suivantes sont identiques : Collection->reject( v : Type | boolean-expression-with-v ) collection->select( v : Type | not (boolean-expression-with-v) )

6.2 L’opération Collect Comme il a été montré dans la section précédente, l'opération select ou reject fournit toujours un résultat sous la forme d'une sous collection à partir de la collection d’origine. Lorsque nous voulons spécifier une collection qui est dérivée à partir d'une autre collection, mais qui contient des objets différents de ceux de la collection d'origine, c'est à dire qui ne constitue pas une sous collection, nous pouvons utiliser l'opération collect. Celle-ci utilise la même syntaxe que les opération select et reject et s'écrit de la façon suivante: collection->collect( v : Type | expression-with-v ) collection->collect( v | expression-with-v ) collection->collect( expression )

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

23

La valeur de l'opération collect est constituée par la collection des résultats de toutes les évaluations de expression-with-v Un exemple: spécifier la collection des dates de naissances (birthDates) pour tous les employés dans le contexte d'une entreprise (company) self.employee->collect( birthDate ) self.employee->collect( person | person.birthDate ) self.employee->collect( person : Person | person.birthDate ) Une question importante ici est que la collection résultat n'est pas un ensemble, mais un sac (Bag). Quand plusieurs employés ont la même date de naissance, cette valeur apparaîtra plusieurs fois dans le résultat. Le sac résultant de l'opération collect a toujours la même dimension que la collection d'origine. Il est possible de construire un ensemble (Set) à partir d'un sac (Bag) en utilisant la propriété asSet sur le sac. L'expression suivante fournit comme résultat l'ensemble (Set) des différentes dates de naissance des employés de l'entreprise: self.employee->collect( birthDate )->asSet Parce que la navigation au travers de plusieurs objets est une action très commune, il existe une notation simplifiée pour l'opération Collect qui rend les expressions OCL plus lisibles. Au lieu de : self.employee->collect(birthdate) on peut écrire: self.employee.birthdate En général, quand on applique une propriété aux objets d'une collection. Celle-ci sera automatiquement interprétée comme un Collect sur les membres de la collection avec la propriété spécifiée. Ainsi, pour tout nom de propriété (propertyname) qui est défini comme une propriété sur les objets de la collection, les deux expressions suivantes sont équivalentes: collection.propertyname collection->collect(propertyname) Également si la propriété est paramètrisée: collection.propertyname(par1, par2, ...) collection->collect(propertyname(par1, par2, ...)

6.3 L'opération forAll A chaque fois qu'une contrainte est nécessaire sur l'ensemble des éléments d'une collection, l'opération forAll permet de spécifier une expression booléenne qui doit être appliquée à tous les objets de la collection. collection->forAll( v : Type | boolean-expression-with-v ) collection->forAll( v | boolean-expression-with-v ) collection->forAll( boolean-expression )

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

24

Cette expression forAll renvoie un booléen. Le résultat est vrai si boolean-expression-with-v est vraie pour tous les éléments de la collection. Si boolean-expression-with-v est faux pour un v ou plus dans la collection, alors l'expression complète est évaluée à faux. Par exemple, dans le contexte Company: context Company inv: self.employee->forAll( forename = 'Jack' ) inv: self.employee->forAll( p | p.forename = 'Jack' ) inv: self.employee->forAll( p : Person | p.forename = 'Jack' ) Ces invariants sont évalués à vrai si la caractéristique forename de chaque employé est égale à «Jack». L’opération forAll possède une variante avec laquelle on peut mettre en œuvre plus d’un itérateur. Deux itérateurs peuvent être utilisés sur la collection complète. C’est un forAll sur le produit cartésien de la collection sur elle-même. context Company inv: self.employee->forAll( e1, e2 | e1 <> e2 implies e1.forename <> e2.forename) context Company inv: self.employee->forAll( e1, e2 : Person | e1 <> e2 implies e1.forename <> e2.forename) Cette expression est évaluée à vrai si les forename de tous les employés sont différents. C’est sémantiquement équivalent à : context Company inv: self.employee->forAll(e1 | self.employee->forAll (e2 | e1 <> e2 implies e1.forename <> e2.forename)))

6.4 L’opération exists Souvent on a besoin de savoir si il y a au moins un élément dans une collection pour laquelle une contrainte existe. L’opération exists permet de spécifier une expression booléenne qui doit convenir pour au moins un objet de la collection : collection->exists( v : Type | boolean-expression-with-v ) collection->exists( v | boolean-expression-with-v ) collection->exists( boolean-expression ) Le résultat de l’opération exists est un booléen. Le résultat est vrai si boolean-expression-with-v est vrai pour au moins un élément de la collection. Si boolean-expression-with-v est évalué à faux pour tout v dans la collection, alors l’expression complète est évaluée à faux. Par exemple dans le context de Company : context Company inv: self.employee->exists( forename = 'Jack' ) context Company inv: self.employee->exists( p | p.forename = 'Jack' ) context Company inv:

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

25

self.employee->exists( p : Person | p.forename = 'Jack' ) Ces expressions sont évaluées à vrai si la caractéristique forename d’au moins un employé est égale à « Jack ».

6.5 L’opération iterate L’opération d’itération (iterate) est légèrement plus complexe, mais très générale. Les opérations select, forAll, exists, collect, elect peuvent toutes être décrites en termes d’itération. Une accumulation construit une valeur par itération sur une collection. collection->iterate( elem : Type; acc : Type = <expression> | expression-with-elem-and-acc ) La valeur elem est l’itérateur, comme dans la définition de select, forAll, etc. La variable acc est l’accumulateur. L’accumulateur prend une valeur initiale <expression>. Quand iterate est évalué, elem réalise une itération sur la collection et expression-with-elem-and-acc est évaluée pour chaque élément. Après chaque évaluation de expression-with-elem-and-acc, sa valeur est assignée à acc. De cette façon, la valeur de acc est construite pendant l’itération de la collection. L’opération collect décrite en termes d’iterate ressemblera à : collection->collect(x : T | x.property) qui est identique à collection->iterate(x : T; acc : T2 = Bag{} | acc->including(x.property)) Ou bien, écrit en pseudo code Java, le résultat de l’itération peut être calculé comme suit: iterate(elem : T; acc : T2 = value)

{ acc = value; for(Enumeration e = collection.elements() ; e.hasMoreElements(); )

{ elem = e.nextElement(); acc = <expression-with-elem-and-acc> }

}

6.6 Itérateurs sur les opérations des collections Les opérations sur les collections qui prennent une OclExpression comme paramètre, doivent toutes avoir une déclaration optionnelle d’itérateur. Pour toute opération nommée op, la syntaxe des options est la suivante : collection->op( iter : Type | OclExpression ) collection->op( iter | OclExpression ) collection->op( OclExpression )

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

26

6.7 Résolution des propriétés Pour toute propriété (attribut, opération ou navigation) la notation complète comprend l’objet dans lequel la propriété est extraite. Comme cela a été vu dans le paragraphe 6.3.3 sur les invariants, self peut être traité de façon implicite. Il en est de même pour les variables d’itérations dans les opérations sur les collections. Dans une expression, quand un itérateur n’est pas explicitement cité, une variable implicite est introduite. Par exemple dans : context Person inv: employer->forAll( employee->exists( lastName = name) ) trois variables implicites sont introduites. La première est self, qui est toujours l’instance à partir de laquelle la contrainte débute. Ensuite, un itérateur implicite est introduit par forAll et un troisième par exists. Ces variables ne sont pas nommées. Les propriétés employer, employee, lastName et name ont toutes un objet sur lequel elles sont appliquées. � A la place d’employer, il y a une variable implicite : self:Person.Par conséquent, employer doit

être une propriété de self. � A la place d’employee, il y a deux variables implicites : self:Person et iter1:Company. Par

conséquent, employer doit être une propriété soit de self soit d’iter1. Si employee est une propriété à la fois de self et d’iter1, alors il n’y a pas d’ambiguïté et l’instance sur laquelle employee est appliquée doit être établit explicitement. Dans ce seul cas, iter1:employee est possible.

� A la place de lastName et name il y a trois variables implicites : self:Person, iter1:Company et iter2:Person. Donc, lastName et name doivent être à la fois des propriétés soit de self, d’iter1 ou d’iter2. La propriété name est une propriété d’iter1. Cependant, lastName est une propriété à la fois de self et d’iter2. C’est ambigu et par conséquent l’expression OCL est incorrecte. L’expression doit établir soit self.lastName ou bien définir l’itérateur iter2 de façon explicite et établir iter2.lastName.

Les deux contraintes suivantes sont correctes : context Person inv: employer->forAll( employee->exists( p | p.lastName = name) ) inv: employer->forAll( employee->exists( self.lastName = name) )

7 Types OCL prédéfinis (voir le document annexe: OCL_Annexe.PDF)

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

27

Index du texte: 1� Préambule................................................................................................................................................. 1�

1.1� Pourquoi OCL?................................................................................................................................. 1�1.2� Comment utiliser OCL...................................................................................................................... 2�

2� Introduction ............................................................................................................................................... 2�2.1� Légende ........................................................................................................................................... 2�2.2� Diagramme de classes..................................................................................................................... 3�

3� Lien avec le méta modèle UML ................................................................................................................ 4�3.1� Self ................................................................................................................................................... 4�3.2� Spécification du contexte UML......................................................................................................... 4�3.3� Les invariants ................................................................................................................................... 4�3.4� Pré et post conditions....................................................................................................................... 5�3.5� Le contexte Package........................................................................................................................ 5�3.6� Expressions générales..................................................................................................................... 6�

4� Valeurs élémentaires et types................................................................................................................... 6�4.1� Types provenant du modèle UML.................................................................................................... 6�4.2� Types énumération........................................................................................................................... 6�4.3� L’expression Let et les contraintes de définition............................................................................. 7�4.4� Conformité de types ......................................................................................................................... 7�4.5� Re typage ou consolidation.............................................................................................................. 8�4.6� Règles de précédence ..................................................................................................................... 8�4.7� Utilisation des opérateurs infixes ..................................................................................................... 9�4.8� Les commentaires ............................................................................................................................ 9�4.9� Valeurs indéfinies ........................................................................................................................... 10�

5� Objets et propriétés................................................................................................................................. 10�5.1� Les propriétés................................................................................................................................. 10�5.2� Propriétés: attributs ........................................................................................................................ 10�5.3� Propriétés: Opérations ................................................................................................................... 11�5.4� Propriétés: Extrémités (rôles) d’associations et navigation ........................................................... 11�

5.4.1� Absence de Nom de rôle ........................................................................................................... 12�5.4.2� Navigation sur des associations avec des cardinalités Zéro ou un........................................... 12�5.4.3� Propriétés combinées ................................................................................................................ 13�

5.5� Navigation et types d’associations ................................................................................................. 13�5.6� Navigation à partir des classes associations ................................................................................. 14�5.7� Navigation au travers des associations qualifiées ......................................................................... 14�5.8� Utilisation des noms de chemins pour les paquetages et les propriétés. ...................................... 15�5.9� Accès aux propriétés des super types ........................................................................................... 15�5.10� Caractéristiques prédéfinies sur tous les objets ............................................................................ 16�5.11� Caractéristiques sur les classes elles-mêmes ............................................................................... 17�5.12� Collections...................................................................................................................................... 17�5.13� Collections de collections............................................................................................................... 19�5.14� Hiérarchie de type collection et règles de conformité de types...................................................... 19�5.15� Valeurs préalables dans les Post Conditions................................................................................. 19�

6� Les opérations sur les collections........................................................................................................... 20�6.1� Les opérations Select et Reject ..................................................................................................... 20�6.2� L’opération Collect.......................................................................................................................... 22�6.3� L'opération forAll ............................................................................................................................ 23�6.4� L’opération exists ........................................................................................................................... 24�6.5� L’opération iterate........................................................................................................................... 25�6.6� Itérateurs sur les opérations des collections.................................................................................. 25�6.7� Résolution des propriétés .............................................................................................................. 26�

7� Types OCL prédéfinis ............................................................................................................................. 26�

_____________________________________________________________________________ Le Langage UML 2.0 Claude Belleil Université de Nantes

28

Index des figures: Figure 1 : Diagramme de classe "exemple" ...................................................................................................... 3�Figure 2 : navigation récursive sur une classe association............................................................................. 13�Figure 3 : Ambiguïté de parcours .................................................................................................................... 15�Figure 4: Propriétés sur un automate à états .................................................................................................. 16�