שפת אילוצי העצמים Object Constraints Language (OCL) 1 Outline What is OCL Types of constraints Constraints structure OCL expressions Examples Textbook: Jos Warmer and Anneke Kleppe, “The Object Constraint Language” Available on Google Books 2 What is OCL It is intended for the specification of additional requirements (constrains) which cannot or be expressed graphically. To specify invariants on classes and types in the class model. To describe pre- and post- conditions on operations. To describe guards. To specify target (sets) for messages and actions. To specify constraints on operations. To specify derivation rules for attributes for any expression over a UML model. 3 Types of Constraints An invariant is a constraint that states a condition that must always be met by all instances of the class, type, or interface. An invariant is described using an expression that evaluates to true if the invariant is met. Invariants must be true all the time (Class diagram). A precondition to an operation is a restriction that must be true at the moment that the operation is going to be executed. The obligations are specified by post conditions (Sequential diagram). A postcondition to an operation is a restriction that must be true at the moment that the operation has just ended its execution (Sequential diagram). A guard is a constraint that must be true before a state transition fires (State machine diagram). 4 Constraints Structure context Typename c_type [name]: <OCL–expression> The context is keyword introduces the context for the expression. Each OCL expression is written in the context of an instance of a specific type. Typename is the name of the type or the class for which the invariant constrain applies. c_type is the type of the constrain: invariant, pre/post-condition, guard. [name] – optional, the names of the constraint. OCL - expression should accept value “true” for all instances of Typename. For example: Context Packagename1::class1 inv : … Context class2 inv : … 5 Basic Data Types Operations above basic types of data (Boolean, Integer, Real, String); Type Boolean Integer Operations and, or, xor, not, if-then-else, … multiply, add, subtract, divide, compare… Real String multiply, add, subtract, divide, compare… toUpper, toLower, concat, … 6 Collections set : the mathematical set. It is not ordered and does not contain duplicate elements. bag : A container. It is not ordered and contain duplicate elements. sequence : An container. It is ordered and contain duplicate elements. collection asSet() : can be used to convert any collection into a set (remove duplicates). 7 implies operator, operator Implies: A implies B A=T, B=T A=T, B=F A=F, B=T A=F, B=F T F T T Operations on collections. Do not mix ‘’ with ‘implies’ as in logical notation! 8 Collection Operations • <collection> → … → isEmpty( ) → notEmpty ( ) → exists ( e:T | <b.e.>) isUnique(e:T | <v.e>) Boolean: → forAll ( e:T* | <b.e.>) forAll(e:T1,f;T2|<b.e.>) → excludes ( object ) → includes ( object ) → includesAll ( collection ) → count ( object ) Numeral: → size ( ) → sum ( ) → select ( e:T | <b.e.>) asSet() Collections: → reject ( e:T | <b.e.>) including(object), excluding(object) → collect ( e:T | <v.e.>) includingAll(collection), excludingAll(collection) Generic: → iterate ( e:T1; r:T2 = <v.e.> | <v.e.>) • b.e. stands for: boolean expression • v.e. stands for: value expression (as seen in the Structural Modeling, Part B lecture (slides 59-60)). 9 Collection Operations - Select select ( e:T | <b.e.>): SQL-like select, returning a sub-collection of the original. animals select(a:Animal | a.family == “Reptile”) reject ( e:T | <b.e.>) is select’s complementary operation. From a collection of animals – to a <= collection of animals! e stands for iterator expression, T stands for type. b.e. stands for: boolean expression v.e. stands for: value expression 10 Collection Operations - Collect collect ( e:T | <v.e.>): from every member of the collection, take something. Thus this operation might return a collection of different type than the original! animals collect(a:Animal | a.name) From a collection of animals– to a collection of strings! Alice Bob Eve Mallory Carol Dave Peggy Victor Trent Trudy Walter Paul Shorthand Notation: students.studentCard – only in ‘collect’!!! 11 OCL – expression Operations above objects: <class>.<attribute> <class>.<role>.<attribute> (preferred) <class>.<class name>.<attribute> Operations above collections: <collection> → <operation> :דוגמאות 1. context Employee inv: 2. 2. 3. 4. self.age>17 and self.age<=100 context Employee inv: 100 - ל18 גיל העובדים חייב להיות בין self.age>30 or ומעלה5 גיל מינימלי של העובדים במחלקות self.workplace.number<5 .31 חייב להיות לפחות context Department inv: self.number<5 or self.employee select(age<=30)size()=0 context Department inv: self.employeeexist(job=“manager”) and self.company.year<=self.year לכל מחלקה חייב להיות "מנהל" ושנת הקמת context Company inv: המחלקה לא מוקדם משנת הקמת החברה self.department.employee פנסיונרים לא מועסקים בחברה forAll(age<=67) .1 .2 .3 .4 12 Example 1: treating collections as instances, enumerations If a person has a wife, she must be female and the person must be male. ⇒ context Person inv: WifenotEmpty() implies (wife.gender=Gender::female and self.gender=Gender::male) //note: after a collection has been validated to be of size 1, it can be treated as an instance! If a person has a husband, he must be male and the person must be female. ⇒ context Person inv: HusbandnotEmpty() implies (husband.gender=Gender::male and self.gender=Gender::female) 13 Example 1 (Cont.) If a person is male he can't have pink car ⇒ context Person inv: (self.gender==Gender::male) implies (self.ownsselect(c|c.color==‘pink’)size()=0) If a person is unemployed he can't own cars in value of more then 500,000$ ⇒ context Person inv: (self.unemployed) implies (self.owns collect(c | c.value) sum()<= 500000) )(אם האדם מובטל אז )(יוצר אוסף של ערכי הרכבים שברשותו )500000-(חשב סכום ערכי הרכבים תבדוק שהוא קטן מ A person can’t have cars in more then 3 different colors ⇒ context Person inv: self.owns collect(c|c.color) asSet() size()<=3 )(יוצר אוסף של צבעי הרכבים שברשותו ) השאר אחד מכל צבע- set (הפוך את האוסף ל ) צבעים3 (ספירת כמות צבעים ובדיקה שאין יותר מ 14 Example 1 (Cont.) A person between the age of 0 to 18 can’t own a yellow car while person between the age of 18 and 21 can own one yellow car at most and a person older then 21 can own as many yellow cars as he like. ⇒ context Person inv: let amountOfYellowCars : Integer = self.ownsselect(c:Car | c.color = “yellow”)size() in if (self.age < 18) then(amountOfYellowCars = 0) else if (self.age < 21) then (amountOfYellowCars <= 1) else true endif endif 15 Example 2 The sum of the values of all securities must be at least 20% more than the amount of the credit. ⇒ context Credit inv sufficientSecurities: securities.Value sum()> self.Amount * 1.2 A credit of a given customer can only be secured by securities that are owned by the given customer. ( )הלוואה של לקוח נתון מאובטחת רק ע"י ערבויות ששייכות לאותו הלקוח ⇒ context Credit inv onlyOwnedSecurities: securities.ownerforAll(aa:Customer|aa = self.customer) aa is instance of owner!!!! 16 Instance and Type Operations Instances have the following operations: self.oclIsTypeOf(T) returns true iff self is of type T self.oclIsKindOf(T) returns true iff (self is of type T) V (self’s subtype of T) Self.oclAsType(T) used for casting (for instance, from Abstract class to Subclass). Types have the ‘allInstances’ member: T.allInstances returns a collection of all T instances. Example: “At least 1 animal has to eat meat”: Animals.allInstances exists(a:Animal|a.eats == “Meat”) In reality, this is usually frowned upon (we’ll see it later!) 17 Example 3: allInstances, type checks, recursion All people should be married ⇒ context Person inv: Person.allInstancesforAll(p:Person | p.married) In a Class all the Attributes have different names. ⇒ context Class inv: self.features select(f|f.oclIsTypeOf(Attribute)) isUnique(a:Attribute|a.name) (collection of Attributes) (collection of Features) 18 Association Class * Navigate to an association class: If the context is Teacher, you could navigate to the association class with the expression: self.Room. This expression would result in a collection of all the room assignments for all the courses to which the teacher is assigned. Navigate from an association class to either end of the association: Starting with a context of the Room class, the expressions: self.instructor self.course is valid. Navigating from an association class always results in a single object. 19 Example 4 - Train All trains must own at least one wagon ⇒ context Train inv atLeastOneWagon: self.wagon size() >= 1 A wagon and its successor wagon should belong to the same train ⇒ context Wagon inv belongToTheSameTrain: self.succ notEmpty() implies self.succ.train = self.train All the trains will have the same number of wagons ⇒ context Train inv sameNumberOfWagons: Train.allInstances forAll(x:Train,y:Train | x.wagonsize() = y.wagonsize() ) 20 Example 4 - Train There do not exists two different wagons directly linked to each other in a cyclic way ⇒ ⇒ not necessary : consider two wagon instances w1,w2. If these two instances are linked with an ‘Order’ link, one has to assume the role of ‘succ’ and the second must assume the role of ‘pred’ – two can’t close a cycle! But how do you prevent a cycle of > 2 wagons? Context Train inv NoCycles: Wagoncollect(w | w.succ)size() = Wagon.size()-1 Will this work? 21 Advanced Topics in OCL Types and Subtypes via oclIsTypeOf and oclIsKindOf 22 Advanced Topics in OCL 23 Advanced Topics in OCL 24 Advanced Topics in OCL Preconditions, Postconditions, Operations (sic, p.155) Navigation into class method using ‘::’ post : result = participants This is how we define the result! Keywords: <attribute>@pre , pre , post , result . 25 Advanced Topics in OCL Avoid allInstances (Warmer and Kleppe, p.67) “A Person has no more than 2 parents:” context Person Inv: parents size() <= 2 context Person Inv: Person.allInstancesforAll(p:Person|p.parentssize()<= 2) “The use of allInstances is discouraged, because it makes the invariant more complex [even to the point of hiding it.]... “In most systems... It is difficult to find all instances of a class. Unless an explicit tracking device keeps a record of all instances of a certain class as they are created and deleted, there is no way to find them. Thus, there is no way to implement the invariant using a programming language equivalent to the allInstances operation. 26 Advanced Topics in OCL And this is just the beginning... Handling message-passing between objects Tuples and Tuple Types Unvefined Values: the OclVoid type Retyping or Casting Unfortunately, these along with many other OCL features are out of the scope of our course. 27