7.4.1 来自UML模型的类型
每个OCL表达式都编写在一个UML模型、一组分类符(类型/类,......)、它们的特征和关联,以及它们的泛化的上下文中。来自UML模型的所有分类符是附加到该模型的OCL表达式中的类型。
7.4.2 枚举类型
枚举是UML中的数据类型,它与任意其它分类符类似都有名称。枚举定义了一组枚举字面量,它们是该枚举的可能值。在OCL中,建模者可以引用枚举的值。当我们拥有示例模型中名为Gender的数据类型(其值为‘female’或‘male’)时,它们可以按如下方式来使用:
context Person inv: gender = Gender::male
7.4.3 Let表达式
有时一个子表达式在一个约束中使用不止一次。let表达式允许建模者定义用在约束中的变量。
context Person inv:
let income : Integer = self.job.salary->sum() in
if isUnemployed then
income < 100
else
income >= 100
endif
let表达式可以被包含在任意种类的OCL表达式中。它只在特定的表达式中使用。let中的变量声明必须有类型和初始值。
7.4.4 通过«definition»表达式声明的其它操作/属性
Let表达式允许在OCL表达式中使用变量。为了在多个OCL表达式中复用变量/操作,建模者可以使用泛型为«definition»的约束,用它来定义变量/操作助手(helper)。«definition»约束必须附加到一个分类符,而且只能包含变量和/或操作定义,不能有其它的。在«definition»约束中定义的所有变量和操作在相同的上下文中是互知的,就如同分类符中的属性一般。这些变量和操作是具有«OclHelper»泛型的分类符属性。它们在OCL表达式中的用法如同普通的属性或操作一般无二。属性或操作定义的语法类似Let表达式,但每个属性和操作定义都使用关键字‘def’做前缀,如下所示。
context Person
def: income : Integer = self.job.salary->sum()
def: nickname : String = ‘Little Red Rooster’
def: hasTitle(t : String) : Boolean = self.job->exists(title = t)
通过“定义表达式”定义的操作或属性可以是静态的(分类符范围的)。此时需要在“def”之前使用static关键字。
context MyClass
static def: globalId(): Integer=...
Let表达式中的属性/操作的名称不能与该分类符的属性、关联端和操作的名称冲突。
使用这种定义语法等同于在UML中使用泛型«OclHelper»定义一个属性/操作,并附加一个衍生的OCL约束。
7.4.5 类型一致性
OCL是一种类型语言,基本的值类型以类型层级方式进行组织。这种层级决定了不同类型之间的一致性。举个例子,你不能拿一个整型与一个字符串进行比较。
如果所有的类型都符合一致性,那么该OCL表达式是有效的。否则就是无效的。当type1的实例可以在任意type2出现地方将其替换掉,那么类型type1就符合类型type2。类图中的类型一致性规则很简单。
- 每个类型符合它的超类类型;
- 类型一致性是传递的:如果type1符合type2,并且type2符合type3,那么type1也符合type3。
OCL标准库中的类型的一致性规则列在表7.3中,其中第三列规定了所涉及类型还需满足的其它条件。
Table 7.3 - - Type conformance rules
类型 | Conforms to/Is a subtype of | Condition |
---|---|---|
Set(T1) | Collection(T2) | 如果T1符合T2 |
Sequence(T1) | Collection(T2) | 如果T1符合T2 |
Bag(T1) | Collection(T2) | 如果T1符合T2 |
OrderedSet(T1) | Collection(T2) | 如果T1符合T2 |
Integer | Real | |
UnlimitedNatural | Integer | * 是一个无效的Integer |
尽管UnlimitedNatural符合Integer,‘*’是一个无效的整型,因此表达式‘1+*’的计算结果是无效的。
集合类型之间的符合性关系只有在元素类型彼此符合的情况下才满足。对于完整的集合一致性规则请参见7.5.13 集合类型层级和类型一致性规则。
表7.4展示了有效和无效表达式示例。
Table 7.4 - - Valid and Invalid Expressions
OCL表达式 | 是否有效 | 解释 |
---|---|---|
1 + 2 * 34 | yes | |
1 + ‘motorcycle' | no | 字符串类型不符合整型 |
23 * false | no | 布尔类型不符合整型 |
12 + 13.5 | yes |
7.4.6 对象类型转换
在有些情况下,需要使用某个对象的当前类型的一个子类型中定义的属性。因为该属性没有在当前已知的类型中定义,这将导致一个类型符合性错误。
当确定该对象的实际类型是这个子类型时,该对象可以使用操作oclAsType(Classifier)进行类型重定义。这个操作产生相同的对象,但其类型变为参数Classifer。当有一个类型为Type1的对象object且Type2是另一个类型,那么允许如下写法:
object.oclAsType(Type2) --- changes the static type of the expression to Type2
对象只能重新类型化为一个它所符合的类型。如果对象的实际类型在计算的时候不是一个所要重类型化类型的子类,那么oclAsType的结果是invalid。
类型转换(casting)在解析时为没有在表达式的静态类型上下文中定义的特征提供了可见性。它并没有把对象转为另一种类型的实例,也不能提供对隐藏的或覆写的类型特征提供访问。为此,要想访问该特征需要限定的类型名(如果需要,是一个路径名),它定义了特征的访问。
举个例子,如果类Employee重定义了Person类中的age() : Integer ,约束可以按如下方式访问Person:
context Employee
inv: self.age() <= self.Person::age()
为了清晰,该限定刑事只能使用一个显式的源表达式。
7.4.7 集合类型转换
集合也可以同样的进行重类型化,但使用集合导航操作:
aCollection->oclAsType(Set(String))
如果aCollection不是一个Set或者它的元素类型不符合字符串都会返回invalid。
集合的元素可以使用collect操作进行单独重类型化:
aCollection->collect(oclAsType(String))
这保留的集合类型,只对元素进行了重类型化。
selectByKind操作可以用于选择一个符合某类型的子集合:
aCollection->selectByKind(Person)
这返回一个与aCollection同类型的子集合,包含了符合Person的所有非空元素。类似的,selectByType返回类型恰为该类型的非空元素的子集合。