January 2017 - Montreal
Simple memo for OCL
Object Constraint Language (OCL) Developed by IBM in 1995
Standardised by OMG in 1997
OCL 2.0 defined in UML 2.0
Allows to add extra information about a model (Like UML diagram)
- inv invariant: constraint must be right at all time
context Employee
inv: self.age > 18
- init: specify an initial value for an attribute
context Employee::wage
init: age = 18
- derive: specify the calculation rule for a derived attribute
context Employee::wage
derive : wage = self.age * 50
All types inherit from OclAny (Like the class Object in Java)
OclVoid can inherit from any type. Its unique instance is null.
- Primitive types (From UML): Boolean / Integer / Real / String
- Collection types: Collection / Set / Bag / Sequence
- User defined types
Let's have Gender enum with values "male" and "female"
context Customer inv:
self.gender = Gender::male
implies self.title = 'Mr.'
Use
->
to apply operations on collections. Example:self.participant->size() <= 25
- Set: mathematics ensemble without duplicates
Set{1,2,5,88}
Set{'apple',''orange','strawberry'}
-
OrderedSet: ordered 'set'
-
Bag is a multi-ensemble (Can have duplicates)
Bag{1,3,45,2,3}
Bag{1,2,3,3,45}
//These 2 Bags are identical
- Sequence: is an ordered Bag
Sequence{1,3,45,2,3}
Sequence{1..10}
Function | Description |
---|---|
size() | |
isEmpty() | |
notEmpty() | |
count(Object) | The number of apparitions of an object in the collection |
includes(Object) | True if the object is an element of this collection |
includesAll(Collection) | True if collection is a sub-ensemble of current collection |
any(bool expr) | Randomly choose an object that satisfy this expression |
sum() | Calculate the sum of all elements for a collection of numbers |
sortedBy(expr) | Create an OrderedSet(Sequence) with all original elements from the Set(Bag) according to the expression |
= | True if all elements from both collections are same. Bag: nb occurrences for each element must be same. Ordered: order must be same. |
union(Collection) | |
intersection(Collection) | |
symmetricDifference(Collection) | Only for Set |
including(Object) | Add object in collection. Set: add if not already in collection. Sequence: equivalents to append(Object) |
append(Object), prepend(Object) | Add element at the end / beginning of the collection |
excluding(Object) | Remove every occurrence of Object |
first(), last() | Return first / last element (Only for ordered collections) |
at(int) | Index start at 1 |
context Company inv:
if self.budget < 50000
then self.employees->size() < 31
else true
endif
iterate(exp) apply expression on each element and put it into a Bag (Or sequence)
context Collection(T)::sum() : T
post: result = self->iterate( element : T;total : T = 0 total + element)
Function | Description |
---|---|
exists(bool expr) | True if expression true for at least one element |
one(bool expr) | True if expression true for exactly one element |
forAll(bool expr) | True if expression true for all elements |
select(bool expr) | Choose all elements that match expression |
reject(bool expr) | Exclude all elements that match expression |
collect(expr) | Calculate expression for each element and put it on a Bag (Or Sequence) |
isUnique(expr) | True if the expression result is unique for each element |
context Company inv:
self.employees->select(age < 18)->isEmpty()
context Company inv:
self.employees->reject(age>=18)->isEmpty()
context Company inv:
self.employees->collect(wage)->sum() < self.budget
context Company inv:
self.employees->forAll(age > 18)
context Company inv:
self.employees->exists(e|e.wage=20000)
Function | Description |
---|---|
oclType() | Return object type |
oclIsKindOf(OclType) | True if object type is OclType or sub class |
oclIsTypeOf(OclType) | Like oclIsKindOf but no sub class |
oclAsType(OclType) | Cast |
allInstances() | Return ensemble of all class instances (To avoid because resources expensive) |
context Employee inv:
Employee.allInstances()->forAll(p1, p2 | p1 <> p2 implies p1.name <> p2.name)
- closure recursive operation
parent.closure(children)
Return a collection including all children, children of children etc...
- let: create sub-expression used more than once in the same constraint
context Person inv:
let income = self.job.salary->sum() in
if isUnemployed then
income < 100
else
income >= 100
endif
- def: variables or operation to use in several constraints
context Person
def: income : Integer = self.job.salary->sum()
inv: if isUnemployed then
income < 100
else
income >= 100
endif
inv: taxes = income * 0.25
- body: specify request result
context Employee::getWage() : Integer
body: self.wage
- pre / post: pre / post condition that must be true before / after operation execution
context Employee::raiseWage(newWage:Integer)
pre: newWage > self.wage
post: wage = newWage
context Person::getCurrentSpouse() : Person
pre: self.isMarried = true
body: self.marriages->select(m | m.ended = false ).spouse
context Person::isSenior() : Boolean
pre: age >= 0
post: age = age@pre
body: age >= 65
//Age is unchanged by function
- @pre refers to property value before operation
context Customer::discount: Integer derive:
if not self.premium then
if self.rental.car.carGroup->
select(c|c.category=‘high’)->size() >= 5
then 15
else 0 endif
else 30 endif
30% discount for premium customer.
15% discount for non premium customer with at least 5 rented autos (With 'high' category)
Otherwise, no discount
context Job inv:
self.employee.age>18 implies
self.employer.isIncorporated = true
If at least one employee is 18 or more, then company must be incorporated