Object Constraint Language

The object constraint language was covered as part of the Software Engineering module (COM00144M) during my MSc studies with the University of York. This post will provide information on why OCL is used and examples of how you can implement OCL.

What is OCL?

The object constraint language (OCL) is a declarative ISO standard language created by IBM. OCL was developed to help relieve limitations with UML modelling and to allow the specification of more precise aspects of a system design. OCL is a part of the Unified Modelling Language (UML) set by the Object Management Group (OMG) as a standard for object-oriented design.

OCL is a declarative and side-effect free language which means the constraints only enforce rules, they do not assign values or modify the system.

Some usages of OCL include:

  • Invariants specify constraints that have to equal true during the instance of an objects lifetime
  • Preconditions specify constraints that have to equal true before a method is run
  • Postconditions specify constraints that have to equal true after a method is run
  • Initialisation specify default property values

This post includes various examples, all of these examples are relative to this conceptual class diagram.

Invariant

An invariant is a constraint that has to be true during the lifetime of an instantiated object. This works as a guarantee that the values of a model always satisfy the requirements.

Invariant Syntax

context <classifier>
    inv [<constraint name>]: <Boolean OCL expression>

Invariant Examples

context Earth
    -- earth has more surface water than surface land
    inv: waterSurfaceArea > landSurfaceArea
    -- earth should only have one moon
    inv: satellite->selectByKind(Moon)->size() = 1
    -- if population less 0, life must exist
    inv: population > 0 implies planet.lifeExists() = true
    -- the name of the earth must consist of 5 characters
    inv: name.size() = 5

context Mars
    -- mars day length is more than 24 hours
    inv: getDayLength > 24
    -- there is no life on mars (that we know of)
    inv: lifeExists() = false

context Human
    -- a human must have an age of at least 0
    inv: age >= 0
    -- all humans must live on a planet
    inv: livesOn->notEmpty()
    -- all humans must live on earth
    inv: livesOn->forEach(p Planet: p.oclIsTypeOf(Earth))

Precondition

A precondition is a constraint that has to be true prior to the running of an operation or method. This works as a guarantee the values are as expected before an alterations take place and the values satisfy the model requirements.

Precondition Syntax

context <classifier>::<operation>> (parameters)
    pre [<constraint name>]: <Boolean OCL expression>

Precondition Examples

context Earth::addHuman(h:Human)
    -- the object being added must be a of Human type
    pre: h.oclIsTypeOf(Human)
    -- population must not already include this object
    pre: population->excludes(h)

context Earth::launchSatellite(s:ManMadeSatellite)
    -- the object being added must be a of ManMadeSatellite type
    pre: s.oclIsTypeOf(ManMadeSatellite)
    -- the earth satellite budget must be less than all satellite cost
    pre: satelliteBudget >= satellite->selectByKind(ManMadSatellite).getCost()->sum() + s.getCost()
    -- this object being added must not already exist as a satellite
    pre: satellite->excludes(s)

context InnerPlanet::hitByAsteroid(a:Asteroid)
    -- the object must be of type Asteroid
    pre: a.oclIsTypeOf(Asteroid)
    
context Earth::hitByComet(c:Comet)
    -- the object must be of type c:Comet
    pre: c.oclIsTypeOf(Comet)
    -- in this example, life exists prior to the Comet hitting
    pre: lifeExists() = true
    pre: population = population->size()

Postcondition

A postcondition is a constraint that has to be true after to the running of an operation or method. This works as a guarantee the values are as expected after alterations take place and the new values satisfy the model requirements.

Postcondition Syntax

context <classifier>::<operation>> (parameters)
    post [<constraint name>]: <Boolean OCL expression>

Postcondition Examples

context Earth::addHuman(h:Human)
    -- the earth population must include the added Human
    post: population->includes(h)

context Earth::launchSatellite(s:ManMadeSatellite)
    -- the earth satellites must include the added s:ManMadeSatellite
    post: satellite->includes(s)

context InnerPlanet::hitByAsteroid(a:Asteroid)
    -- the planets mass will increase by the mass of the Asteroid
    post: mass = mass@pre + a.getMass()
    -- the amount of craters will have increased
    post: craterCount = craterCount@pre + 1

context Earth::hitByComet(c:Comet)
    -- all life will cease to exist
    post: lifeExists() = false
    -- the population will be zero
    post: population = 0
    -- the size of the population collection will be zero
    post: population->size() = 0

String Operations

A set of standard operations that can be used on strings. They are used in OCL expressions using a period symbol followed by the operation: attribute.size().

size(): Integer
Returns the number of characters in self

concat(s:string) : String
Returns the concatenation of self and s

substring(s:String) : String
Returns the sub-string of self

toUpperCase() : String
Returns string as uppercase

toLowerCase() : String
Returns string as lowercase

The full list of operations can be found on p.160 of the OCL Spec.

Collection Operations

A set of standard operations that can be used on collections. They are used in OCL expressions using a hyphen and arrow: collection->size().

size: Integer
Returns number of elements in the collection

includes(o:OclAny) : Boolean
Returns true if element o exists in the collection

excludes(o:OclAny) : Boolean
Returns true if element o does not exist in the collection

count(o:OclAny) : Integer
Returns how many times an element is contained in the collection

isEmpty: Boolean
Returns true if the collection is empty

notEmpty: Boolean
Returns true if the collection is not empty

including(o:OclAny)
Collection containing all elements of the Collection plus element o

excluding(o:OclAny)
Collection containing all elements of the Collection excluding(o:OclAny)

select(expr:oclExpression)
Subset of all elements of the collection, for which the OCL-expression expr is true

sum: Integer | Real | UnlimitedNatural
Returns addition of all elements within self

exists(v: Type | boolean-expression-with-v) 
exists(v | boolean-expression-with-v) 
exists(boolean-expression) 
Specify a Boolean expression that must hold true for at least one object in a collection

forAll(v: Type | boolean-expression-with-v)
forAll(v | boolean-expression-with-v)
forAll(boolean-expression)
Specify a Boolean expression that must hold true for all objects in a collection

The full list of operations can be found on p.28 of the OCL Spec.

Type Operations

A set of standard operations that can be used in relation to the type of an object.

oclIsTypeOf(t:Classifier): Boolean
An operator used to check if self and t are of the same type but not a subtype

oclIsKindOf(t:Classifier): Boolean
An operator used to check if self and t are of the same type or a subtype

The full list of operations can be found on p.153 of the OCL Spec.

Leave a Reply

Your email address will not be published.