
2.7.5 集合操作(Collection Operation)
OCL定义了丰富的集合类型和操作,运用这些集合操作,可以写出明确和无二义性地表达模型中与集合有关的较为复杂的规则和约束。
本节将选择若干个典型的集合操作来介绍一下集合操作的基本使用方法。
1.枚举操作(Iterate Operation)
枚举操作是一个基本的集合操作。OCL中其他集合操作,如筛选(Reject)、选择(Select)、所有(For All)、存在(Exists)和收集(Collect)操作等都与枚举操作有关。
枚举操作的语法格式定义如下。

其中,collection是集合对象,是集合操作的承受者;iterate是枚举操作的操作名;变量elem:Type是迭代器,表示集合collection中的每个元素;变量acc:Type被称为累加器,expression表示累加器的初值;Type是集合元素类型。枚举操作工作时,迭代器将依次取值集合的每个元素,同时,对每个迭代器elem和累加器acc计算表达式(expression-with-elem-and-ac c)的值,并赋值给累加器acc。迭代过程结束后,累加器变量的值将被作为操作结果返回。
枚举操作可以有很多灵活的应用。例如,对于图2-30中的类图,编写一个能够描述“顾客所有订单的金额的总和”的OCL表达式。这个操作只要枚举顾客的每一张订单并累加每张订单的金额即可,不难写出下面的表达式。

这个表达式似乎并不完整,进一步完善后,可得出下面的表达式。

此时,得到了一个不变量,其含义是“顾客的订单总额不能是负数”。这显然也可以看成是一个定义在顾客对象上的约束。其内容也包含了“顾客所有订单的金额的总和”的描述。
2.选择操作(Select Operation)
使用OCL表达式中的操作或导航可能会产生一个特定的集合,但有时可能仅对其中某些特殊元素感兴趣,此时,对集合中的元素进行必要的筛选就变得很有意义了。
选择(Select)操作,就是指从一个特定集合中选择满足指定条件的元素,并以集合的形式返回被选择的元素。
在OCL中,Select操作可以有简单、含迭代器和一般三种表示形式。
1)Select操作的基本格式。

其中,collection是表达式中的集合对象,<condition>是指定的查询条件。
2)使用含有迭代器变量的形式。

其中,v是定义在collection上的迭代器变量,用于枚举collection中的每个元素。表达式中“|”前面的部分相当于迭代器变量的声明,“|”后面的部分则是一个引用了迭代器变量v的布尔表达式(boolean-expression-with-v),用于检查每次迭代枚举到的元素。迭代过程中,每个评估值为真的元素加入到将作为操作结果的集合中。
3)Select操作的一般表示形式。

一般形式与含迭代器变量的形式相同,不同的是它标明了迭代器变量的类型。
例如,描述“年龄大于50岁的员工集合是一个非空集合”的OCL表达式可以写成如下三种形式。
1)简单形式。

2)含迭代器形式。

3)一般形式。

建模时,在不引起歧义的情况下,可选择比较简单的形式。与Select操作类似,OCL还定义了一个筛选(Reject)操作。Reject操作主要用于从集合中滤掉满足指定条件的元素,并返回未被过滤掉的元素的集合。这个操作具有与Select操作完全相同的表示形式,具体用法与Select操作完全相同。
Reject操作的三种语法表示形式定义如下。

3.收集操作(Collect Operation)
前面讨论的选择和筛选操作的结果总是原始集合的子集。当想要从原始集合派生出某个特定的集合,但结果却不再是原始集合的子集时,就可以使用Collect操作了。
Collect操作的含义是对于给定的表达式,计算原始集合中每个元素的表达式值,并将计算结果收集到一个集合中,最后返回这个集合。
Collect操作的语法格式与Select和Reject相似,可以写成如下的三种形式。

例如:当需要使用“公司所有员工的出生日期”这样的一个集合时,就可以使用收集操作来描述这样的操作,可以写成如下的OCL表达式,并可应用在任何一个以Company为上下文的OCL表达式中。
“公司所有员工出生日期”集合可以用三种形式表示,它们的解读方式与Select操作完全一样,具体表示如下。

它们的具体语义就不再过多了。
还有一个重要问题是收集(Collect)操作的结果类型问题。前面介绍过,集合是一个抽象类型,集合类型被分为Bag、Set和Sequence三种类型。因此,任何一个集合都只能是Bag、Set和Sequence这三种类型之一。
对与Collect操作,OCL特别做了一个约定,如表2-4所示。
表2-4 Collection操作结果类型

在这个约定中,Collect操作结果的类型中没有Set类型。如果希望Collect操作结果的类型是Set类型,可以使用Bag类型的asSet操作将结果转换成Set类型,此时得到的结果将可能会发生必要的变化。
例如当公司的员工的出生日期值出现重复时,出生日期值将在选择操作结果中重复出现。Collect操作产生的Bag类型的集合的大小总是与原始集合的大小完全相同。但使用asSet属性转换后的结果就不再相同了。
例如,当希望收集“公司员工有多少个不同的生日”以支持某业务规则时,就可以使用下面的表达式来描述这样的集合。这个表达式所描述的集合中,出生日期就是不重复的。

4.所有操作(For All Operation)
在很多情况下,约束可能需要定义在集合的所有元素上。OCL的For All操作将允许这样的检查。所有ForAll操作的用途是检查集合元素是否满足指定条件,当集合的所有元素都满足指定条件时,表达式返回真。
所有(For All)操作同样具有如下三种形式。

例如,下面的OCL表达式就定义了一个表达“所有员工的年龄均不超过65岁”的不变量。

For All操作还可以有一个扩展的迭代器变量,即同时使用多个迭代器。每个迭代器都将完整地遍历整个集合。实际上,这样的ForAll操作可以被等价看成是一个定义在集合的笛卡尔积上的For All操作。
例如,下面的表达式就使用了扩展形式的For All操作,它所表达的语义是“不同的员工的forename属性值也不相同”。

这个表达式只有在所有员工的forname属性均不相同时才会为真,它等价于下面的OCL表达式。

5.存在操作(Exists Operation)
当需要知道集合中是否存在至少一个满足某个条件的元素时,可以使用存在(Exists)操作。
Exists操作主要检查集合中是否存在满足指定操作的元素,当集合中至少有一个满足条件的操作时表达式返回真。Exists操作的语法格式如下。

例如,描述“公司有名字叫作Jack的员工”的OCL表达式可以写成如下形式。
