context Site inv:
self.enterprise -- retourne l'objet 'e' de type 'Enterprise' ou le singleton '{e}'
self.enterprise.profile -- retourne un ensemble d'objets 'Profile'
context Enterprise inv:
site.contact -- 'self' peut être omis, retourne un ensemble d'objets 'Contact'
context Organizational profile inv:
profile -- retourne l'objet 'p' de type 'Profile' ou le singleton '{p}'
contact -- interdit !
context Contact inv:
affiliation -- retourne l'objet 's' de type 'Site' ou le singleton '{s}'
affiliation.enterprise.status -- retourne un 'bag' (un seul membre) d'objets 'Legal status'
Legal status::Trust -- accès à une valeur de type énuméré
Les profils d'un site en tant qu'organisation sont inclus dans l'ensemble des profils de la compagnie de ce site :
context Site inv:
enterprise.profile->includesAll(profile)
Si l'on réunit tous les profils du site et de la compagnie d'un contact donné, on sait que les rôles que joue ce contact appartiennent nécessairement à la réunion* :
context Contact inv:
(affiliation.enterprise.profile->union(affiliation.profile))->
includesAll(organizational profile.profile)
Unicité :
context Person inv:
Person.allInstances()->isUnique(PIN) -- 'PIN' est un attribut de 'Person'
*Sur-spécification
String
, Boolean
, Integer
(conforme à Real
⤳ substituabilité) et Real
Collection
(classe abstraite), Bag
, Sequence
, Set
et OrderedSet
Set(Vélo)
se conforme à Collection(Moyen de transport)
si Vélo
se conforme à Moyen de transport
Tuple
, OclMessage
, OclExpression
…AnyType
avec une seule instance, OclAny
qui est lui-même un type dont hérite les autres types OCL à l'exception des types Collection
InvalidType
, VoidType
…Person.allInstances()
returns Set(Person)
context Person inv:
let FranckBarbier : Person = Person.allInstances()->select(PIN = ‘1630125388055’) in
FranckBarbier.oclIsTypeOf(Man) = true
FranckBarbier.oclIsTypeOf(Person) = false
FranckBarbier.oclIsKindOf(Man) = true
FranckBarbier.oclIsKindOf(Person) = true
FranckBarbier.oclAsType(Man).beard = true
context Woman::bear() : Person
pre: oclInState(Pregnant::Delivery)
post: result.oclIsNew()
post: result.oclIsTypeOf(Man) implies sons->size() = sons@pre->size() + 1
post: result.oclIsTypeOf(Woman) implies daughters->size() = daughters@pre->size() + 1
Exprimer en OCL* : «  frères » ainsi que le fait que
l'être humain dont le n°SS est 1630125388055
a une sœur, pas de frère et deux nièces !
*Les éléments (logiques) du langage de base sont not
, true
, false
, and
, or
et implies
context Être humain::frères : Collection(Être humain)
oclAsType(Homme).frère->union(oclAsType(Femme).frère)
context Être humain::frères : Collection(Être humain)
if not oclAsType(Homme).oclIsUndefined()
then oclAsType(Homme).frère
else oclAsType(Femme).frère
context Être humain::frères : Collection(Être humain)
self.homme.frère->union(self.femme.frère)
oclIsUndefined()
et oclIsInvalid()
oclIsUndefined()
correspond au test de null
, unique instance de OclVoid
oclIsInvalid()
correspond au test de invalid
, unique instance de OclInvalid
oclAsType(Homme).frère->union(oclAsType(Femme).frère)
oclAsType(Homme).sœur->union(oclAsType(Femme).sœur)
*OCL
context Moteur inv:
not self.voiture.oclIsUndefined() implies self.camion.oclIsUndefined()
Les opérations OCL sont par définition sans effet de bord (query) ; OCL est en effet un langage déclaratif !
Contenu d'une opération :
context Enterprise::employees() : Set(Contact)
body: site.contact
Pré and post-conditions :
context Distributeur automatique bancaire::distribuer(montant : Integer)
pre: montant = 20 or montant = 40 -- Etc.
pre: en stock >= montant
post: en stock = en stock@pre - montant
def
& let
Clause def
(accessibilité depuis l'extérieur, i.e., export) :
context Être humain def:
sexe : String = self.n°SS.substring(1,1)
Clause let
(contextuelle à expression) :
context Être humain inv:
let sexe : String = self.n°SS.substring(1,1) in
if sexe = '1' then
-- etc.
else
-- etc.
endif
attr
, init
& derive
Clause attr
:
context Être humain def:
attr proches : Tuple(frères: Set(Homme),sœurs: Set(Femme)) =
Tuple {oclAsType(Homme).frère->union(oclAsType(Femme).frère),
oclAsType(Homme).sœur->union(oclAsType(Femme).sœur)}
Clauses init
(valeur initiale ou par défaut) et derive
:
context Enterprise::status
init: Legal status::Sole proprietorship
derive: if stock quotation = true then
Legal status::Trust
endif
Soit c
de type Collection(Être humain)
:
c->iterate(x | …) -- est la forme générique (par commodité, il y a des formes simplifiées)
c->isUnique(n°SS) ≍ id. (retourne 'Boolean')
*c->select(x | x.n°SS.substring(1,1) = ‘1’) -- retourne 'Collection(Être humain)'
*c->reject(x | x.n°SS.substring(1,1) = ‘2’) -- retourne 'Collection(Être humain)'
c->forAll(x | …) -- ≍ ∀ (retourne 'Boolean')
c->exists(x | …) -- ≍ ∃ (retourne 'Boolean')
c->one() -- ≍ ∃! (retourne 'Boolean')
c->any(not (n°SS.oclIsUndefined() and n°SS.substring(2,3) = ‘63’))
-- retourne un et un seul élément au hasard de la collection
*Oubli du test oclIsUndefined()
Soit c
de type Collection(X)
:
c->size() -- retourne 'Integer'
c->includes(x) -- retourne 'Boolean'
c->excludes(x) -- retourne 'Boolean'
c->count(x) -- retourne 'Integer'
c->includesAll(c’) -- retourne 'Boolean' avec 'c’' de type 'Collection(X)'
c->excludesAll(c’) -- retourne 'Boolean' avec 'c’' de type 'Collection(X)'
c->isEmpty() -- retourne 'Boolean'
c->notEmpty() -- retourne 'Boolean'
c->sum() -- retourne 'X' avec 'X' supportant l’opération +
c->product(c’) : Set(Tuple(first : X,second : Y)) avec 'c’' de type 'Collection(Y)'
context Route touristique inv:*
let x : OrderedSet(Segment de route touristique) = self.segment de route touristique in
x->iterate(i : Segment de route touristique | i <> x->last() and
let j : Segment de route touristique = x->subOrderedSet(indexOf(i) +
1,x->last())->first() in i.arrivée = j.départ)
*Tous les segments de route touristique d’une route touristique donnée,
sont tels que chacun doit avoir comme ville de départ, la ville d’arrivée de son prédécesseur dans l’ordre posé par la contrainte {ordered}
OclMessage
context Composition.composé::emergent()
post: composant.provided.ownedOperation->forAll(o |
let messages : Sequence(OclMessage) =
self.part^^o() in messages->isEmpty()) -- part axiomatique
context Composition.composé::resultant()
post: composant.provided.ownedOperation->exists(o |
let messages : Sequence(OclMessage) =
self.part^^o() in messages->notEmpty()) -- part axiomatique
context State inv:
isComposite = true implies region->size() >= 1
isOrthogonal = true implies region->size() >= 2
isSimple = true implies region->isEmpty() and submachine->isEmpty()
isSubmachineState = true implies submachine->notEmpty()
OCL :
context Collaborateur::agrégation(société : String) :
Bag(Tuple(ville : String, somme_des_âges : Integer))
body: Collaborateur.allInstances()->select(c | c.société =
société)->collect(ville)->asSet()->collect(v |
Tuple {ville : String = v, somme_des_âges : Integer =
Collaborateur.allInstances()->select(c | c.société = société
and c.ville = v).âge->sum()})
SQL :
SELECT Collaborateur.ville, sum(Collaborateur.age) FROM Collaborateur
WHERE Collaborateur.societe = ‘ALE’ GROUP BY Collaborateur.ville;