Vous êtes sur la page 1sur 35

Object Constraint Language

(OCL)
• The Object Constraint Language (OCL) is a declarative
language for describing rules that apply to UML.
• Initially, OCL was only a formal specification language
extension to UML.
• OCL may now be used with any Meta Object Facility
(MOF),Object Management Group (OMG) meta model
, including UML.
• The Object Constraint Language is a text language that
provides constraint and object query expressions on
any MOF model or meta-model.
• OCL is a key component of the new OMG standard
recommendation for transforming models, the
Queries/Views/Transformations (QVT) specification.
History of OCL
• Developed at IBM in 1995 originally as a business
• engineering language
• • Adopted as a formal specification language
within UML
• • Part of the official OMG standard for UML (from
• version 1.1 on)
• • Current version is OCL 2.0
• Constraint
Definition
– constraint is a restriction on one or more
values of (part of) an object-oriented model or
system.
– A constraint is formulated on the level of
classes, but its semantics is applied on the
level of objects.
– Originally formulated in the syntactic context
of a UML.
Types of expressions in OCL
• Expressions can be used in a number of places in a
UML model:
– To specify the initial value of an attribute or association
end.
– To specify the derivation rule for an attribute or
association end.
– To specify the body of an operation.
– To indicate an instance in a dynamic diagram.
– To indicate a condition in a dynamic diagram.
– To indicate actual parameter values in a dynamic diagram.
Types of constraints in OCL
• There are four types of constraints:
– An invariant
– A precondition
– A postcondition
– A guard is a constraint that must be true before a state
transition fires.
The Context of an OCL Expression

• The context definition of an OCL expression specifies


the model entity for which the OCL expression is
defined.
– Usually this is a class, interface, datatype, or component.
In terms of the UML standard, this is called a Classifier.
• Sometimes the model entity is an operation or
attribute, and rarely it is an instance.
– It is always a specific element of the model, usually
defined in a UML diagram. This element is called the
context of the expression.
Contextual type of an OCL Expression
• Next to the context, it is important to know the contextual
type of an expression. The contextual type is the type of the
context, or of its container.
• It is important because OCL expressions are evaluated for a
single object, which is always an instance of the contextual
type.
• To distinguish between the context and the instance for which
the expression is evaluated, the latter is called the contextual
instance.
• Sometimes it is necessary to refer explicitly to the contextual
instance. The keyword self is used for this purpose.
inv: transaction.card.owner->size() = 1

LoyaltyAccount
init: 0
points: Integer
number: Integer
earn(i: Integer)
pre: i > 0 burn(i: Integer)
isEmpty(): Boolean
1 Account
body: points = 0
Transactions 0..*
init: Set { }
Transaction
points: Integer
date: Date
program() :
LoyaltyProgram
Invariants on attributes

• The simplest constraint is an invariant on an


attribute.
• Suppose our model contains a class Customer with
an attribute age, then the following constraint
restricts the value of the attribute:

context Customer inv:


age >= 18
Invariants on associations

• One may also put constraints on associated objects.


• Suppose a model contains the class Customer has an
association to class Salesperson, with role name
salesrep and multiplicity 1, then the following
constraint restricts the value of the attribute
knowledgelevel of the associated instance of
Salesperson:

context Customer inv:


salesrep.knowledgelevel >= 5
Collections of objects
• In most of the cases the multiplicity of an association is not 1, but more
than 1.
• Evaluating a constraint in these cases will result in a collection of instances
of the associated class.
• Constraints can be put on either the collection itself, e.g. limiting the size,
or on the elements of the collection.
• Suppose in our model the association between Salesperson and Customer
has role name clients and multiplicity 1..* on the side of the Customer
class, then we might restrict this relationship by the following constraint.

context Salesperson inv:


clients->size() <= 100 and
clients->forAll(c: Customer | c.age >= 40)
Pre- and postconditions
• In pre- and postconditions the parameters of the operation
may be used.
• there is a special keyword result which denotes the return
value of the operation.
• It can be used in the postcondition only.
• As an example we have added an operation sell to the
Salesperson class.

context Salesperson::sell( item: Thing ): Real


pre: self.sellableItems->includes( item )
post: not self.sellableItems->includes( item ) and result = item.price
Derivation Rules
• Models define derived attributes and associations.
• A derived element does not stand alone.
• The value of a derived element must always be determined from other
(base) values in the model.
• Omitting the way to derive the element value results in an incomplete
model.
• Using OCL, the derivation can be expressed in a derivation rule.
• In the following example, the value of a derived element usedServices is
defined to be all services that have generated transactions on the account:

context LoyaltyAccount::usedServices : Set(Services)


derive: transactions.service->asSet()
Initial Values
• In the model information, the initial value of an
attribute or association role can be specified by an
OCL expression.
• In the following examples, the initial value for the
attribute points is 0, and for the association end
transactions, it is an empty set:

context LoyaltyAccount::points : Integer


init: 0
context LoyaltyAccount::transactions :
Set(Transaction)
init: Set{}
Initial Values
• Note the difference between an initial value and a
derivation rule.
– A derivation rule states an invariant:
– The derived element should always have the same
value that the rule expresses.
– An initial value, however, must hold only at the
moment when the contextual instance is created.
After that moment, the attribute may have a
different value at any point in time.
Body of Query Operations
• The class diagram can introduce a number of query operations.
• Query operations are operations that have no side effects, i.e., do
not change the state of any instance in the system.
• Execution of a query operation results in a value or set of values,
without any alterations in the state of the system.
• Query operations can be introduced in the class diagram, but can
only be fully defined by specifying the result of the operation.
• Using OCL, the result can be given in a single expression, called a
body expression.
• OCL is a full query language, comparable to SQL. The use of body
expressions is an illustration thereof.
• The next example states that the operation getCustomerName
will always result in the name of the card owner associated with
the loyalty account:
context LoyaltyAccount::getCustomerName() : String
body: Membership.card.owner.name
Flight 0..* 1 Airplane
flightNo: Integer numberOfSeats: Integer
flights plane
availableSeats(): Integer
flights 0..*

passengers 0..*
Person
name: String

• an association between class Flight and class Person, indicating that a


certain group of persons are the passengers on a flight, will have
multiplicity many (0..*) on the side of the Person class.
• This means that the number of passengers is unlimited.
• In reality, the number of passengers will be restricted to the number of
seats on the airplane that is associated with the flight.
• It is impossible to express this restriction in the diagram.
Flight 0..* 1 Airplane
flightNo: Integer numberOfSeats: Integer
flights plane
availableSeats(): Integer
flights 0..*

passengers 0..*
Person
name: String

In this example, the correct way to specify the multiplicity is to add to the
diagram the following OCL constraint:

context Flight
inv: passengers->size() <= plane.numberOfSeats
Person
House 0..* 1 IDCardNo: Integer
value: Money
houses owner salary: Money

security 1 getMortgage(sum: Money


security: House)
mortgages 0..*
1 borrower
Mortgage
principal: Money
monthlyPayment: Money 0..*
startDate: Date mortgages
endDate: Date

• A person may have a mortgage on a house only if that house is


owned by him- or herself;
– one cannot obtain a mortgage on the house of one's neighbor or friend.
• The start date for any mortgage must be before the end date.
• The ID card number of all persons must be unique.
Person
House 0..* 1 IDCardNo: Integer
value: Money
houses owner salary: Money

security 1 getMortgage(sum: Money


security: House)
mortgages 0..*
1 borrower
Mortgage
principal: Money
monthlyPayment: Money 0..*
startDate: Date mortgages
endDate: Date

• A new mortgage will be allowed only when the person's income is


sufficient.
• A new mortgage will be allowed only when the counter-value of the
house is sufficient.
Value Added by OCL
context Mortgage
inv: security.owner = borrower

context Mortgage
inv: startDate < endDate

context Person
inv: Person::allInstances()->isUnique(socSecNr)

context Person::getMortgage(sum : Money, security : House)


pre: self.mortgages.monthlyPayment->sum() <= self.salary * 0.30

context Person::getMortgage(sum : Money, security : House)


pre: security.value >= security.mortgages.principal->sum()
• The Object Constraint Language is just a specification
language.
• It obeys a syntax and has keywords.
• However, unlike other languages, it can't be used to express
program logic or flow control.
• By design, OCL is a query-only language; it can't modify the
model (or executing system) in any way.
• It can be used to express preconditions, postconditions,
invariants (things that must always be True), guard conditions,
and results of method calls.
• OCL can be used virtually anywhere in UML and is typically
associated with a classifier using a note.
• When an OCL expression is evaluated, it is considered to be
instantaneous, meaning the associated classifier can't change
state during the evaluation of an expression.
Constraints on Classifiers
• Each OCL expression must have some sense of context that an expression
relates to.
• Often the context can be determined by where the expression is written.
• For example, you can link a constraint to an element using a note.
• You can refer to an instance of the context classifier using the keyword
self.
• For example, if you had a constraint on Student that their GPA must
always be higher than 2.0, you can attach an OCL expression to Student
using a note and refer to the GPA as follows:

self.GPA >= 2.0


Course
School
title: String
name: String 1..*
roomAssignment: String
tuition: float textBook: String
courseLevel: int

registerStudents(s:Student) :boolean 0..*


getHonorsStudents() :Student[0..*] preReqs
1..* courses

1..* students 1 instructor

Student Teacher

GPA: float name: String


dateOfBirth: Date salary: float
yearOfGraduation: int
tuitionPaid: boolean
Constraints
• if you had a constraint on Student that their GPA
must always be higher than 2.0, you can attach an
OCL expression to Student using a note to refer to
the GPA as follows:
self.GPA > 2.0
• Note:
– If you want to allow a GPA of less than 2.0 and send out a
letter to the student’s parents in the event such a low GPA
is achieved, you would model such behavior using a UML
diagram such as an activity or interaction diagram.
• The following invariant on Course ensures that
the instructor is being paid:
self.instructor.salary > 0.00
Business Rule
• The following expressions verify that a student’s
tuition was paid before registering for a course
and that the operation registerStudent returned
true:

context Course::registerStudent(s: Student): boolean


pre: s.tuitionPaid = true
post: result = true
• We can name pre and post conditions by
placing a label after the pre or post keywords:

context Course::registerStudent(s: Student): boolean


pre hasPaidTuition: s.tuitionPaid = true
post studentHasRegistered: result = true
• Postconditions can use the @pre keyword to refer to the value
of some element before an operation executes.
• The following expression ensures that a student was registered
and the number of students in the course has increased by 1.

context Course::registerStudent(s: Student): boolean


pre hasPaidTuition: s.tuitionPaid = true
post studentHasRegistered: result = true AND
self.students = self.students@pre + 1
• We may specify the results of a query operation using
the keyword body.
• Because OCL doesn’t have syntax for program flow, we
are limited to relatively simple expressions.
• The following expressions indicate that the honors
students are students with GPAs higher than 3.5.

context Course::getHonorsStudents(s: Student): boolean


body: self.students->select(GPA > 3.5)
Conditionals
• OCL supports basic Boolean expression evaluation using the
if-then-else-endif keywords.
• The conditional are used only to determine which expression is evaluated;
they can’t be used to influence the underlying system or to affect program
flow.
• The following invariant enforces that a student’s year of graduation is valid
only if she has paid her tuition:

context Student inv:


if tuitionPaid = true then
yearOfGraduation = 2005
else
yearOfGraduation = 0000
endif
OCL’s Logic Rules
• The boolean evaluation rules are:

1. True OR-ed with anything is true.


2. False AND-ed with anything is false.
3. False IMPLIES anything is True.
OCL’s Logic Rules
• For example, the following expression enforces that if a student's GPA is
less than 1.0, their year of graduation is set to 0. If the GPA is higher than
1.0, Rule #3 applies, and the entire expression is evaluated as true
(meaning the invariant is valid).

context Student inv:


self.GPA < 1.0 IMPLIES self.yearOfGraduation = 0000
• OCL supports several complex constructs you can use to make
your constraints more expressive and easier to write.
• Break complex expressions into reusable pieces (within the same
expression) by using the let and in keywords to declare a variable.
• declare a variable by giving it a name, followed by a colon (:), its
type, an expression for its value, and the in keyword.
• The following example declares an expression that ensures a
teacher of a high-level course has an appropriate salary:
context Course inv:
let salary : float = self.instructor.salary in
if self.courseLevel > 4000 then
salary > 100000.00
else
salary < 100000.00
endif

Vous aimerez peut-être aussi