Chapitre4: Object Constraint Language (OCL)
Cours Ingénierie Dirigée par les modèles Cours Ingénierie Dirigée par les modèles
Niveau: II3 ILSI Enseignante: Rim DRIRA [email protected] Bureau 215
AU: 2016/2017
Plan
1. Pourquoi OCL
2. Contexte d’une contrainte
3. Syntaxe d’OCL
2
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
OCL (Object Constraint Language)
UML permet d’exprimer différents types de contraintes:
contraintes structurelles: les attributs, les différents types de relations
entre classes, cardinalités, etc.
contraintes de type: le typage des attributs, etc. contraintes prédéfinies sur les associations: {subset}, {xor}, etc. contraintes en langue naturelle (commentaires) contraintes en langue naturelle (commentaires) diverses autres contraintes comme les contraintes de visibilité, des
classes abstraites, etc.
TOUTEFOIS
Certaines contraintes ne peuvent pas être formalisées avec UML.
Afin de combler cette lacune, l’OMG a proposé un langage d’expression
de contraintes appelé OCL.
3
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Pourquoi OCL ? Illustration par l'exemple
Application bancaire qui manipulent les classes Compte,
Personne et Banque
1.
2.
3.
4.
5.
6.
7.
8.
Un client peut posséder plusieurs comptes
Un compte doit avoir un solde toujours positif.
Un client peut être client de plusieurs banques
Un client d'une banque possède au moins un compte dans cette banque banque
Les clients de la banque associée à un compte doivent contenir le propriétaire de ce compte
Une banque gère plusieurs comptes
Une banque possède plusieurs clients
Les méthodes qu’on pourra appliquer sur Compte sont: créditer(int), débiter(int) et int getSolde()
Quelles sont les spécifications non exprimées par le
diagramme de classe proposé??
4
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Pourquoi OCL ? Illustration par l'exemple
Le diagramme de classe ne permet pas d’exprimer:
2. Un compte doit avoir un solde toujours positif
4. Un client d'une banque possède au moins un compte dans cette
banque
5. Les clients de la banque associée à un compte doivent 5. Les clients de la banque associée à un compte doivent
contenir le propriétaire de ce compte
5
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Pourquoi OCL ? Illustration par l'exemple
Diagramme d'objets cohérent avec le diagramme de classes et la
spécification:
6
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Pourquoi OCL ? Illustration par l'exemple
Diagramme d'objets cohérent avec le diagramme de classes mais
qui ne respecte pas la spécification attendue.
Une personne a un compte dans une banque où elle n'est pas cliente
Une personne est cliente d'une banque mais sans y avoir de compte
7
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Diagrammes UML insuffisants
Pour spécifier complètement une application
Diagrammes UML seuls sont généralement insuffisants
Nécessité de rajouter des contraintes
Comment exprimer ces contraintes ?
Langue naturelle mais manque de précision, compréhension pouvant
être ambigüe
Langage formel avec sémantique précise : OCL
8
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
OCL, c’est quoi?
Langage de description de contraintes d'UML, peut s’appliquer sur
différents diagrammes UML
Standardisé par l'OMG
Version 2.3.1: http://www.omg.org/spec/OCL/ISO/19507/PDF/
Version actuelle: 2.5 http://www.omg.org/spec/OCL/2.5
Objectif: Spécifier des contraintes supplémentaires importantes qui n'ont pas pu être spécifiés avec les éléments de modélisation d'UML
S'applique aussi sur MOF
9
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Utilisation d’OCL
OCL permet principalement d'exprimer deux types de contraintes sur l'état
d'un objet ou d'un ensemble d'objets:
Invariant de classe: une contrainte qui doit être respectée en permanence
par toutes les instances d'une classe.
pré-condition et post-conditions à l'exécution d'une opération:
Permet aussi d’exprimer: Permet aussi d’exprimer:
contraintes sur la valeur retournée par une méthode
règles de dérivation des attributs
Gardes sur transitions de diagrammes d'états ou de messages de diagrammes
de séquence/communication
Etc.
10
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Contexte d’une contrainte OCL
Une contrainte OCL est toujours associée à un élément d’un
modèle, il est appelé le contexte de la contrainte.
Exemple :
Publicité
context Compte : l'expression OCL s'applique à la classe Compte, c'est-à-dire
à toutes les instances de cette classe à toutes les instances de cette classe
. permet d'accéder à une caractéristique d'un objet (méthode,
attribut, terminaison d'association)
Exemple : banque.clients donne l’ensemble d’objets de type client
associé à une banque
11
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Retour à l’Exemple
Exprimer le fait que le solde d’un compte doit être positif ne peut pas être fait avec UML mais peut être formalisé avec la contrainte OCL suivante :
Context Compte
Inv: solde>0
Exprimer le fait que les clients de la banque associée à un compte doivent contenir le propriétaire du compte avec OCL:
Context Compte
Inv: banque.clients -> includes (propriétaire)
12
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Pré et post conditions
Pour spécifier une opération
Précondition : état qui doit être respecté avant l'appel de l'opération
Postcondition : état qui doit être respecté après l'appel de l'opération
Mots-clés : pre et post
Dans la postcondition, deux éléments particuliers sont utilisables Dans la postcondition, deux éléments particuliers sont utilisables Attribut result : référence la valeur retournée par l'opération
mon_attribut@ pre : référence la valeur de mon_attribut avant l'appel de
l'opération
Syntaxe pour préciser l'opération
context ma_classe::mon_op(liste_param) : type_retour
13
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Pré et post conditions
Exemple1
context Compte::débiter(somme : Integer)
pre: somme > 0
post: solde = solde@pre – somme
La somme à débiter doit être positive pour que l'appel de l'opération soit valide
Après l'exécution de l'opération, l'attribut solde doit avoir pour valeur la différence de sa valeur Après l'exécution de l'opération, l'attribut solde doit avoir pour valeur la différence de sa valeur
avant l'appel et de la somme passée en paramètre avant l'appel et de la somme passée en paramètre
Exemple2
context Compte::getSolde() : Integer
post: result = solde
Le résulat retourné doit être le solde courant
Attention! On ne décrit pas comment l'opération est réalisée mais des contraintes sur l'état
avant et après son exécution
14
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Conception par contrat pour opérations
Pre et postconditions permettent de faire une conception par
contrat
Contrat passé entre l'appelant d'une opération et l'appelé (celui qui
exécute l'opération)
Si l'appelant respecte les contraintes de la précondition alors l'appelé Si l'appelant respecte les contraintes de la précondition alors l'appelé
s'engage à respecter la post-condition
Si l'appelant ne respecte pas la précondition, alors le résultat de
l'appel est indéfini
Pour exemple précédent: Si l'appelant de l'opération débiter passe une somme positive en paramètre, alors le compte est bien débité de cette somme
15
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Invariant
Un invariant exprime une contrainte sur un objet ou un groupe
d'objets qui doit être respectée en permanence
Mot-clé : inv
Exemple: context Compte inv: solde > 0
Pour toutes les instances de la classe Compte, l'attribut solde doit
toujours être positif
16
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Accès aux objets, navigation
Dans une contrainte OCL associée à un objet, on peut
Accéder à l'état interne de cet objet (ses attributs) Naviguer dans le diagramme : accéder de manière transitive à tous les objets
(et leur état) avec qui il est en relation
Nommage des éléments
Pour les attributs ou paramètres d'une opération, on utilise le nom Pour les attributs ou paramètres d'une opération, on utilise le nom
directement
Pour le(s) objet(s) en association, on utilise le nom du rôle d'association du côté de cette classe (dans les exemples, s’il n’y a pas de noms de rôles, je vais utiliser le nom en minuscule de la classe (comme OCL 2.2)) Si cardinalité de 1 ou 0..1 pour une association : référence un objet Si cardinalité > 1 : référence une collection d'objets
17
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Accès aux objets, navigation
Exemples, dans le contexte de la classe Compte
solde : attribut référencé directement banque : objet de la classe Banque (référence via le nom de la classe) associé
au compte
propriétaire : objet de la classe Personne (référence via le nom de rôle
d'association) associée au compte d'association) associée au compte
banque.clients : ensemble des clients de la banque associée au compte
(référence par transitivité)
banque.clients.age : ensemble des âges de tous les clients de la banque
associée au compte
Le propriétaire d'un compte doit avoir plus de 18 ans context Compte inv: propriétaire.age >= 18
18
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Navigation vers une classe association
Dans contexte Entreprise
Self.Contrat
Dans contexte Personne
Self.Contrat
Comment naviguer vers l’objet Encadrement de son responsable ?
o
self.Encadrement [responsable]
Comment naviguer vers l’objet Encadrement de son agentSecrétariat ?
o
self.Encadrement [agentSecrétariat]
19
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Opérations sur objets et collections OCL propose un ensemble de primitives utilisables sur les collections
size() : retourne le nombre d'éléments de la collection isEmpty() : retourne vrai si la collection est vide notEmpty() : retourne vrai si la collection n'est pas vide includes(obj) : vrai si la collection inclut l'objet obj excludes(obj) : vrai si la collection n'inclut pas l'objet obj including(obj) : la collection référencée doit être cette collection en incluant l'objet including(obj) : la collection référencée doit être cette collection en incluant l'objet
Publicité
obj
excluding(obj) : idem mais en excluant l'objet obj includesAll(ens) : la collection contient tous les éléments de la collection ens excludesAll(ens) : la collection ne contient aucun des éléments de la collection ens Sum(): addition de tous les éléments de la collection count(obj) : retourne le nombre d'occurences d'un objet dans la collection
Syntaxe d'utilisation : objetOuCollection -> primitive
self: pseudo-attribut référençant l'objet courant
20
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Opérations sur objets et collections
Exemples, invariants dans le contexte de la classe Compte
propriétaire -> notEmpty() : il y a au moins un objet Personne
associé à un compte
propriétaire -> size() = 1 : le nombre d'objets Personne associés à
un compte est de 1 un compte est de 1
banque.clients -> size() >= 1 : une banque a au moins un client
banque.clients -> includes(propriétaire) : l'ensemble des clients de la banque associée au compte contient le propriétaire du compte
banque.clients.compte -> includes(self) : le compte appartient à
un des clients de sa banque
21
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Opérations sur objets et collections
Autre exemple
context Banque :: creerCompte(p : Personne) : Compte
post: result.oclIsNew() and compte = compte@pre -> including(result) and p.compte = p.compte@pre -> including(result) and result.proprietaire = p and result.banque = self
Un nouveau compte est créé. La banque doit gérer ce nouveau compte. Un nouveau compte est créé. La banque doit gérer ce nouveau compte.
Le client passé en paramètre doit posséder ce compte. Le nouveau compte
est retourné par l'opération.
OclIsNew()
Primitive indiquant qu'un objet doit être créé pendant l'appel de l'opération
(à utiliser dans une postcondition)
22
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Relations ensemblistes entre collections
union: retourne l'union de deux collections
intersection: retourne l'intersection de deux collections
Exemples
(col1 -> intersection(col2)) -> isEmpty() renvoie vrai si les collections (col1 -> intersection(col2)) -> isEmpty() renvoie vrai si les collections
col1 et col2 n'ont pas d'élément en commun
col1 = col2 -> union(col3) La collection col1 doit être l'union des éléments
de col2 et de col3
23
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Opérations sur les éléments d'une collection
OCL permet de vérifier des contraintes sur chaque élément d'une collection ou de définir une sous-collection à partir d'une collection en fonction de certaines contraintes
Primitives offrant ces services et s'appliquant sur une collection col
select : retourne le sous-ensemble de la collection col dont les éléments respectent
la contrainte spécifiée reject : idem mais ne garde que les éléments ne respectant pas la contrainte reject : idem mais ne garde que les éléments ne respectant pas la contrainte collect : retourne une collection (de taille identique) construite à partir des
éléments de col. Le type des éléments contenus dans la nouvelle collection peut être différent de celui des éléments de col.
exists : retourne vrai si au moins un élément de col respecte la contrainte spécifiée
et faux sinon
forAll : retourne vrai si tous les éléments de col respectent la contrainte spécifiée
(pouvant impliquer à la fois plusieurs éléments de la collection)
24
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Opérations sur les éléments d'une collection
Syntaxe de ces opérations : 3 usages
collection -> primitive( expression )
La primitive s'applique aux éléments de la collection et pour chacun d'entre eux, l'expression expression est vérifiée. On accède aux attributs/relations d'un élément directement.
collection -> primitive( elt : type | expression)
On fait explicitement apparaître le type des éléments de la collection (ici
type). On accède aux attributs/relations de l'élément courant en utilisant elt (c'est la référence sur l'élément courant)
collection -> primitive(elt | expression)
On nomme l'attribut courant (elt) mais sans préciser son type
25
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Opérations sur les éléments d'une collection
Exemples dans le contexte de la classe Banque:
compte -> select( c | c.solde > 1000) Retourne une collection
contenant tous les comptes bancaires dont le solde est supérieur à 1000 € compte -> reject( solde > 1000) Retourne une collection contenant tous les comptes bancaires dont le solde n'est pas supérieur à 1000 € compte -> collect( c : Compte | c.solde) Retourne une collection compte -> collect( c : Compte | c.solde) Retourne une collection
contenant l'ensemble des soldes de tous les comptes
(compte -> select( solde > 1000 )) -> collect( c | c.solde)
Retourne une collection contenant tous les soldes des comptes dont le solde est supérieur à 1000 €
inv: not( clients -> exists (age < 18) )
Il n'existe pas de clients de la banque dont l'age est inférieur à 18 ans not Prend la négation d'une expression
26
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Opérations sur les éléments d'une collection
Autre Exemple:
context Personne inv: Personne.allInstances() ->
forAll(p1, p2 | p1 <> p2 implies p1.nom <> p2.nom)
Il n'existe pas deux instances de la classe Personne pour lesquelles
l'attribut nom a la même valeur : deux personnes différentes ont un l'attribut nom a la même valeur : deux personnes différentes ont un nom différent
AllInstances()
Primitive s'appliquant sur une classe (et non pas un objet) et
retournant toutes les instances de la classe référencée (ici la classe Personne)
27
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Types OCL : types de base
Exemples
context Personne inv: if age < 18 -- vérifie age de la personne then compte -> isEmpty() -- pas majeur : pas de compte else compte -> notEmpty() -- majeur : doit avoir au moins un compte Endif context Personne inv: compte -> notEmpty() implies banque -> notEmpty() -- Si une personne possède au moins un compte bancaire, alors elle est cliente d'au
moins une banque
28
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Enumération
Utilisation d'une valeur d'une énumération
NomEnum::valeur
Exemples:
context Homme context Homme
inv: sexe = Gender::male
context Femme
inv: sexe = Gender::female
29
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Propriétés prédéfinies en OCL
Pour objets
Publicité
oclIsTypeOf(type) : l'objet est du type type
oclIsKindOf(type) : l'objet est du type type ou un de ses sous-types
oclInState(état) : l'objet est dans l'état état oclInState(état) : l'objet est dans l'état état
oclIsNew() : l'objet est créé pendant l'opération
oclAsType(type) : l'objet est « casté » en type type
oclIsUndefined() : la propriété (association par exemple) n'a pas
été initialisée (équivalent d'un « null »)
30
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Règles de précédence
Ordre de précédence pour les opérateurs/primitives du plus au moins prioritaire
@pre
. et ->
not et -
* et /
+ et - + et -
if then else endif
>, <, <= et >=
= et <>
and, or et xor
implies
Parenthèses permettent de changer cet ordre
31
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Variables
Pour faciliter l'utilisation de certains attributs ou calculs de valeurs on peut
définir des variables
Dans une contrainte OCL : let ... in ...
context Personne
inv: let argent = compte.solde -> sum() in age >= 18 implies argent > 0
Une personne majeure doit avoir de l'argent
Pour l'utiliser partout : def
context Personne
def: argent : Integer = compte.solde -> sum()
context Personne
inv: age >= 18 implies argent > 0
32
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
OCL: Bilan
Permet d’exprimer des contraintes supplémentaires qu’il n’est pas
possible d’exprimer avec UML
Langage formel mais simple à utiliser, non ambigu, grammaire précise
Langage déclaratif : les contraintes ne sont pas opérationnelles.
Une expression OCL décrit une contrainte à respecter et non pas un code à Une expression OCL décrit une contrainte à respecter et non pas un code à
exécuter
Langage sans effet de bord : les instances ne sont pas modifiées par
les contraintes.
Peut être utilisé au niveau M3 (sur Ecore, MOF, UML), M2(sur UML et sur les méta-modèles MOF et Ecore), M1 (diagrammes UML classe, état, etc.)
33
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Exercices Exercices
34
Exercice1
Ajoutez un attribut mère de type Personne
dans la classe Personne.
Ecrivez une contrainte précisant que la mère d'une personne ne peut être cette personne elle-même et que l'âge de la mère doit être elle-même et que l'âge de la mère doit être supérieur à celui de la personne
Solution:
context Personne inv:
self.mere <> self and self.mere.age > self.age
35
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Exercice2
Indiquez qu’une personne mariée est forcément majeur
Solution: context Personne inv: marié implies majeur
Trouvez une version plus compacte de l’expression suivante
context Personne inv : if age >=18 then majeur=vrai else majeur=faux endif Solution: context Personne inv: majeur = age >= 18
36
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Exercice3
Les deux contraintes suivantes sont-elles équivalentes ?
Contexte C1 inv: c2->forAll(c:C2| c.c3->notEmpty() implies c.attr2=c.c3.attr3)
Contexte C2 inv: C2.allInstances()->forAll(c:C2| | c.c3->notEmpty() implies
c.attr2=c.c3.attr3)
Solution
NON : la première dit que pour des instances de C2 et C3 liées avec une 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, ces deux
attributs sont égaux.
37
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Exercice4
Exprimer en OCL les contraintes suivantes:
1.
2.
3.
4.
38
Dans une compagnie, un manager doit travailler et avoir plus de 40 ans. Le nombre d'employé d'une compagnie est non nul
Une personne considérée comme au chômage ne doit pas avoir de salaires et une personne qui travaille doit avoir au moins un revenu de 500 DT par mois.
Dans le contexte de la classe Job: l’âge de l’employé doit être supérieur à 21.
Tous les enfants d'une personne ont bien cette personne comme parent et inversement
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017
Exercice4
1. context Company
inv: self.manager.isUnemployed = false and self.manager.age > 40 and self.job -> notEmpty()
2. context Person
Inv: let money : Real = self.job.salary->sum() in if isUnemployed then money =0 else money >= 500 Endif Endif Ou bien context Person inv: if isUnemployed=true then self.job ->isEmpty() else self.job.salary->sum()
>= 500
3. context Job inv: self.employee.age > 21 4. context Person inv: children -> notEmpty() implies children -> forAll ( p :
Person |p.parents -> includes(self))
context Person inv: parents -> forAll ( p : Person |p.children -> includes (self))
39
R.DRIRA ENSI-II3 ILSI Chapitre4: OCL
2016-2017