7.3 与UML元模型的关系
7.3.1 Self
每个OCL表达式都有一个特定类型实例的上下文。在OCL表达式中,关键字self用于指示上下文实例。例如,如果上下文是Company,那么self指的是Company的一个实例。
7.3.2 规定UML上下文
UML模型中OCL表达式的上下文可以在该表达式的开始处通过上下文声明来规定。
如果约束通过适当的泛型和虚线连接到了上下文元素,那么就不需要在约束的文本中显示的声明上下文。上下文声明式可选的。
7.3.3 不变式
OCL表达式可以是一个Invariant(不变式)的一部分,不变式是stereotype为«invariant»的约束。当不变式与一个分类符关联时,后者被称为“类型”。OCL表达式是该类型的一个不变式,必须保障该类型的所有实例在任意时刻都为真。(注意,所有表示不变式的OCL表达式的类型都是Boolean。)
举个例子,如果在图7.1中的Company类型上下文中,如下的表达式将规定一个不变式,指示员工数总是超过50:
self.numberOfEmployees > 50
其中self是类型Company的一个实例。(我们可以把self视为计算该表达式的起始对象。)该不变式对于Company类型的所有实例都要为真。
OCL表达式的上下文实例的类型是不变式的一部分,它通过关键字context来表示,后面跟着类型名。标签inv:声明该约束是一个«invariant»约束。
context Company inv:
self.numberOfEmployees > 50
在大多数情况下,关键字self可以去掉,因为上下文很明显,比如如上的例子。作为self的另一种表示,可以定义一个起self作用的名称。例如:
context c : Company inv:
c.numberOfEmployees > 50
该不变式与前一个是等价的。
可选的,约束的名称可以写在inv关键字的后面,从而使得该不变式通过名称被引用。如下的实例中约束名为enoughEmployees。
context c : Company inv enoughEmployees:
c.numberOfEmployees > 50
7.3.4 前置和后置条件
OCL表达式可以是一个前置或后置条件的一部分,分别对应与一个操作或其它行为特征关联的约束的«precondition» 和 «postcondition»。此时上下文实例self是拥有该操作或方法的类型的一个实例。OCL中上下文声明使用context关键字,后面跟着类型和操作声明。约束的stereotype(泛型)使用标签’pre:'和‘post:'来标明,它们在真正的前置和后置条件前面。例如:
context Typename::operationName(param1 : Type1, ... ): ReturnType
pre : param1 > ...
post: result = ...
self可以用于在表达式中指示操作被调用的对象。保留字result指示操作的结果,如果有一个的话。参数名称(param1)也可以用于OCL表达式,在示例图中,我们可以写:
context Person::income(d : Date) : Integer
post: result = 5000
可选的,前置和后置条件的名称可以加在pre和post关键字后,从而允许该约束通过名称被引用。在下面的示例中前置条件的名称是parameterOK,后者条件名称是resultOK。在UML元模型中,这些名称是从ModelElement继承而来的元类Constraint的name属性的值。
context Typename::operationName(param1 : Type1, ... ): ReturnType
pre parameterOk: param1 > ...
post resultOk : result = ...
7.3.5 包上下文
上面的上下文声明在分类符所属于的包在环境中是很明显的情况下是足够精确的。为了规定不变式、前置或后置约束所属的包,这些约束可以被封装在’package‘和’endpackage'声明内。包声明具有如下的语法:
package Package::SubPackage
context X inv:
... some invariant ...
context X::operationName(..)
pre: ... some precondition ...
endpackage
一个OCL文件(或流)可以包含任意数量的包声明,从而允许所有的不变式、前置和后置条件写在一个文件中。这个文件可以作为一个单独的实体与一个UML模型共存。
7.3.6 操作体表达式
OCL表达式可以用于指示一个检索操作的结果。这可以使用如下的语法来完成:
context Typename::operationName(param1 : Type1, ... ): ReturnType
body: -- some expression
该表达式必须遵循操作的返回类型。与前置和后置条件类似,参数可以用于表达式。前置、后置条件,以及操作体表达式可以在操作上下文中混合在一起。例如:
context Person::getCurrentSpouse() : Person
pre: self.isMarried = true
body: self.mariages->select( m | m.ended = false ).spouse
7.3.7 初始和衍生值
OCL表达式可以用于指示属性或关联端的初始或衍生值。这可以通过如下的语法来完成:
context Typename::attributeName: Type
init: -- some expression representing the initial value
context Typename::assocRoleName: Type
derive: -- some expression representing the derivation rule
这些表达式必须遵循该属性的结果类型。当上下文是关联端是,该表达式必须遵循该端分类符(并且multiplicity最多是1),或者当multiplicity多于1个时遵循集合或有序集合。初始和衍生表达式可以在一个上下文中混合,例如:
context Person::income : Integer
init: parents.income->sum() * 1% -- pocket allowance
derive: if underAge
then parents.income->sum() * 1% -- pocket allowance
else job.salary -- income from regular job
endif
衍生约束必须在任意时刻都要满足,因此该衍生包括了初始化。二者允许规定在在相同的属性上但不能相互矛盾。对于每一个属性最多只能有一个初始化约束和最多一个衍生约束。
7.3.8 其它表达式类型
任意的OCL表达式可以用做UML元类Expression或它的一个子类的一个属性的值。此时,语义子章节部分描述了该表达式的含义。Expression的一个特殊子类,ExpressionInOCL可以用于该目的。参见12.1的引言来查看它的定义。