OCL言語はいくつかの式からなります。式は複数の演算子とオペランドを持った文からなり,結果の値を返します。式はすべて特定の文脈に関係します。文脈は,その式が適用される定義されたモデルの一部です。文脈は,そのルールがどのオブジェクトの集合を制約するのか(たとえば,典型的には,あるクラスのすべてのオブジェクト,あるいはある関連に参加しているすべてのオブジェクト)を決定したり,そのモデル中にOCLによってナビゲーションが始まるところを定義します。contextというキーワードに続けて文脈の名前(たとえば,その式が適用されるクラス名)を書いて文脈を指定します。たとえば,次のようになります。
context Order
式は常に型を持ちます。オブジェクトに適用されて値を返します。たとえば,式の型がBooleanならば,真か偽の値を返します。図5.2は小さなUMLモデルで,Order(注文)クラスにOCL式が付いています。
context Order inv:
sum > 10
この例は,OCLの制約は文脈Orderのクラス不変表明です。不変状態は,OCLキーワードinv:に続く不変式で指定されます。これは常に論理式になります。この論理式は,そのクラスのオブジェクトすべてについて真であると評価されなければなりません(この例では,Orderオブジェクトのsum属性はすべて10を超える値が設定されていなければなりません)。テキストでOCL式を書くときは,文脈は行を分けて,式の上に書きます。OCL式を直接UMLダイヤグラムに定義するときは,式をその文脈のそばに置きます(文脈にノートが付けられることもあります)。すなわち,テキストの場合,文脈指定行を書く必要はありません(図5.2を参照)。クラス,関連などの関係,操作,属性などを式の文脈にすることができます。
この他に,Orderクラスにありそうなルールは次のようなものです。
context Order inv:
fee = 0.01 * sum
図5.2 OCL式は常に特定の文脈を参照している
これは属性に対する不変表明で,間接的にfee属性がどう計算されるかを指定しています(この例では,手数料は注文の合計金額の1パーセントであるというものです)。
制約に名前を付けて,その名前で参照できます。次に示すように制約の名前を指定します(ここではfeeCalculation)。
context Order inv feeCalculation:
fee = 0.01 * sum
属性などのプロパティが式で使用されるとき,そのプロパティは文脈のクラスに属していると暗黙に仮定されています。selfキーワードを使用することによって,この関係を明示できます。selfキーワードは,そこで文脈となっている型のインスタンスへの参照です。この例では,文脈はOrderクラスで,selfキーワードはその式が適用されるOrderオブジェクトを参照しています。次のOCL式は前述のものと同値です。
context Order inv:
self.fee = 0.01 * self.sum
selfキーワードは,複数のクラスのプロパティを含む式の中で使用され,どのプロパティが文脈クラスに属しているかを明示します。この式のself参照は,Orderクラスのインスタンスを指しています。
2つのハイフン(--)でコメントの始まりを示します。このコメントはその行の終わりまで続きます。
context Order inv:
self.fee = 0.01 * self.sum -– 手数料の計算
OCL式は,クラスや属性だけでなく,操作にも適用できます。操作に対する制約やルールは,事前条件と事後条件で指定されます。たとえば,図5.2のArticle(品物)クラスに付けられたノートで指定されているルールには,事後条件が指定されています。
context Article::deliverable() : Boolean
pre : --
post: result = (availability = #inStock)
この例では,式の文脈はArticleクラスのdeliverable操作と,この操作の戻り値の計算方法を示している事後条件です。式の型はその操作の戻り値と同じで,この場合はBooleanです。事後条件が指定されているとき,式の中に戻り値を記述するために,OCLのresultキーワードが使用されます。このルールでは,当該Articleオブジェクトがavailability属性にinStockなる値が設定されているとき,操作は真を返すとしています。availability属性は,inStockとoutOfStockという値を持つ列挙型です。OCLでは,列挙型の定数は,属性と取り違えないように先頭にポンドまたは番号記号,すなわちシンボル(#)を付けることになっています。
次は,操作に対する制約のもう1つの例で,返される値(Real)がsum属性を使ってどう計算されるかを指定しています。
context Order::tax() : Real
pre :
post: result = sum * 0.065
OCL式で使用される型は事前に定義された基本型(Boolean,Integer,Real,String),Collection型(Set,Bag,Sequence),そしてユーザが定義したそのモデルに定義される型(すなわちクラス)です。基本型とその演算子を表5.1に記載します。
OCLの基本型はすべて,その式中に定数値を含められます。たとえば,10(Integer),3.14(Real),true(Boolean),’こんにちは’(String)のようにです。演算子の多くはよく知られた算術演算子あるいは文字列演算子なので,ここではこれ以上コメントしないことにします。2つの論理演算子(impliesとif-then-else)については,少し注意が必要です。
implies演算子はBooleanオペランドを2つ取り,最初のオペランドが偽と評価されるか,両方のオペランドがともに真と評価されるときに,真と評価されます。これは,「最初のオペランドが真と評価されたとき,次のオペランドも真と評価されなければならない」と読みます。もし最初のオペランドが偽ならば,2番目のオペランドがどう評価されようと関係ありません(結果は真となります)。たとえば,
context Order inv:
ordered_articles->size = 0 implies sum = 0
このOCL式は,「注文件数が0ならば注文金額の合計は0でなければならない」と読みます。ordered_articlesに使われている矢のシンタックス(->)は,sizeがコレクションのプロパティであるために必須です(コレクションについては,後ろの「コレクション」の項で詳しく述べるつもりです)。ordered_articlesの件数が0でないときは,合計の値にかかわらず,式は真と評価されます。最初の式は,それが真ならば2番目の式も真でなければならないことをimply(含意)しています。
if-then-else構造は,最初のオペランドとしてBoolean式を取ります。それが真ならば,then節の式が評価されて全体の式の結果になります。偽ならば,else節の式が評価されて式の結果になります。たとえば,
context Order inv:
fee = if (sum < 100) then 2.00 else (sum * 0.01) endif
このthen節の式は単純な定数値であり,else節は計算になっています。両方の式は同じ型でなければなりません。
OCLには,厳しい型一致ルールがあります。異なる型の演算子を任意に混在させることはできません。たとえば,IntegerはStringと比較することはできませんし,RealにBooleanを加算することはできません。ただし,RealにIntegerを加えることは可能です。何の情報も失わずにIntegerをRealに変換できる(IntegerはRealのサブタイプ)からです。さらに,OCLの評価順序は明確に規定されたセマンティクスを持っています。つまり,複合式において演算子が評価される順序が規定されているのです。
OCL式は,UMLモデルで定義されたクラスなどの型のプロパティを使用できます。式には,クラスに定義されている属性,操作,関連,および列挙型が含まれます。OCL式で
表5.1 OCLの基本型とその演算子
型 |
演算子と結果の型 |
演算子の説明 |
Boolean |
b1
or b2 -- Boolean |
論理和 |
(true,
false) |
b1
and b2 -- Boolean |
論理積 |
|
b1
xor b2 -- Boolean |
排他的論理和 |
|
not
b1 -- Boolean |
否定 |
|
b1
= b2 -- Boolean |
等しい |
|
b1
<> b2 -- Boolean |
等しくない |
|
b1
implies b2 -- Boolean |
含意する |
|
if b1 then <式1> else <式2>
-- 式1または式2の型 |
if then else |
Integer |
i1
= i2 -- Boolean |
等しい |
(自然数) |
i1
<> i2 -- Boolean |
等しくない |
|
i1 < i2 -- Boolean |
より小 |
|
i1 > i2 -- Boolean |
より大 |
|
i1 <= i2 -- Boolean |
以下 |
|
i1 >= i2 -- Boolean |
以上 |
|
i1 + i2 -- Integer |
加算 |
|
i1 - i2 -- Integer |
減算 |
|
i1 * i2 -- Integer |
乗算 |
|
i1 / i2 -- Real |
除算 |
|
i1 mod i2 -- Integer |
モジュロ |
|
i1 div i2 -- Integer |
整数除算 |
|
i1.abs -- Integer |
絶対値 |
|
i1.max(i2) -- Integer |
最大値 |
|
i1.min(i2) -- Integer |
最小値 |
Real |
r1 = r2 -- Boolean |
等しい |
(実数) |
r1 <> r2 -- Boolean |
等しくない |
|
r1 < r2 -- Boolean |
より小 |
|
r1 > r2 -- Boolean |
より大 |
|
r1 <= r2 -- Boolean |
以下 |
|
r1 >= r2 -- Boolean |
以上 |
|
r1 + r2 -- Real |
加算 |
|
r1 - r2 -- Real |
減算 |
|
r1 * r2 -- Real |
乗算 |
|
r1 / r2 -- Real |
除算 |
|
r1.abs -- Real |
絶対値 |
|
r1.max(r2) -- Real |
最大値 |
|
r1.min(r2) -- Real |
最小値 |
|
r1.round – Integer |
整数に四捨五入 |
|
r1.floor -- Integer |
整数に切り捨て |
String |
s1.concat(s2) -- String |
結合 |
(文字の連続) |
s1.size -- Integer |
文字列の長さ |
|
s1.toLower -- String |
小文字に変換 |
|
s1.toUpper -- String |
大文字に変換 |
|
s1.substring(i1,i2) -- String |
文字列の抜き出し |
|
s1 = s2 -- Boolean |
等しい |
使われる操作はオブジェクトを変えることができないので,値を問い合わせたり計算したりする操作だけが使われます(これは,UMLにおいてisQueryタグ付き値を真に設定する操作として形式的に定義されます)。
次の式では,注文総額の計算の一部にtax操作が使われています。よって,この操作はオブジェクトを変えずに税額を問い合わせる操作でなければなりません。
context Order::totalCost() : Real
post: result = sum + fee + self.tax()
selfキーワードがtax操作の前に使われていることに注意してください。これは必須ではありませんが,tax操作が文脈のオブジェクトを呼んでいることをより明確に示しています。文脈中の操作以外に,関連クラス中の操作もナビゲーション式によって使用できます。
1つのOCL式が,複数の制約で使われることもあります。次の例はOCLのlet文です。これによって,指定された式を参照するために複数の制約で使うことのできる変数を定義しています。
context Order inv:
let feeCharge : Real =
if (sum < 100)
then 2.00 else (sum * 0.01) endif in
fee = sum + feeCharge + self.tax( )
let文を使う代わりに,OCL式で使うためだけの操作をクラス中に定義することもできます。このような操作は,実装操作の一部ではなく仕様操作であることを図式的に示すために,<<oclOperation>>としてステレオタイプ化されなければなりません。しかし,こうした操作が見つかったということは,往々にして,そのクラスの具体的な実装の一部としても興味があるに違いないことを示しています。すなわち,そのような操作は,OCL式の中だけでなく,実行可能なシステムでも使われるだろうということです。通常は,クラス中にOCL操作を定義するよりも,let文の方が好まれます。
図5.3 関連のロール名は文脈から他のクラスへナビゲートするのに使用される
オブジェクトが,指定されたパラメタの型(oclIsTypeOf),またはそれと互換な型(oclIsKindOf)であるならば,oclIsTypeOfとoclIsKindOf操作は真を返します。これらはスーパークラスを文脈とする式中に使用できますが,操作はオブジェクトのその正確なサブタイプに付けられます。たとえば(AirMailOrderとSurfaceOrderはOrderクラスの
サブクラスであるとして),次の例を見てください。
context Order
inv: self.oclIsTypeOf(AirMailOrder) implies fee = 10.00 * (0.01 * sum)
inv: self.oclIsTypeOf(SurfaceOrder) implies fee = 5.50 + (0.01 * sum)
これらは,このOCL式が適用される実際のオブジェクトがAirMailOrderオブジェクトならば,料金は10ドルの基本料と合計額の1%でなければならないこと,オブジェクトがSurfaceOrderならば,料金は5.50ドルと合計額の1%になるということを示す2つの不変表明です。inv:ラベルはOrderの文脈の不変表明ごとに,繰り返されなければならないことに注意してください(そして,このラベルは式の先頭に置かれます。それは文脈指定と同じ行に置く必要はありません)。
OCLでは,式はその最初の文脈から関連する他のクラスにナビゲートできます。他のクラスというのは,その文脈クラスとの関連または集約関係にあるものです。たとえば,図5.3を見て,Order文脈から顧客を識別して,その顧客が18才を超えていることを,次の不変表明によってチェックしていることがわかります。
context Order inv:
customer_of_order.age > 18
この式は,OrderクラスからCustomerクラスへ関連をたどって行って,年齢属性が18才超であることを保証します。ただし,この例では,わざわざナビゲーションせずとも単にCustomerクラスに不変表明を入れた方が適切だったかもしれません。でも,1つの式で複数の異なるクラスの属性を参照するときは,そうではありません。Customerに不変表明を与えることには少し違った意味があります。この場合,CustomerがOrderにリンクを持っているかどうかに関係なく,Customerのインスタンスは,どれも18才を超えていなければならないという意味になります。上の例で示した制約の場合は,Orderにリンクを持っているCustomerだけが制約の対象になります。
関連のロール名は,ナビゲーション経路を指示するのに使用されます。この例では,customer_of_orderです。ロール名が存在しないときは,代わりに関連の相手側のクラス名を小文字で表すことができます(この場合は「customer」)。また,いま問題にしているダイヤグラム中の関係を参照するだけでなく,他のパッケージ中のモデル要素を指すこともできます。
図5.3には,もう1つのOCL式があります。
context Customer inv:
orders->size > 1
この式には,CustomerクラスからOrderクラスへのナビゲーションがあります。その方向の関連の多重度が2以上なので,式はオブジェクトのコレクションになります(注文の明細はコレクション)。このコレクションに標準のsizeプロパティが適用されて,上の不変表明は,顧客には1つ以上の注文が付いていることを保証します(すなわち,顧客であるためには,少なくとも1つの注文がなければなりません)。sizeなどのプロパティは,どのコレクションにも適用できます(コレクションのプロパティと操作の一覧は次節で示します)。sizeプロパティは,関連の相手側(ロール)に存在するインスタンスの個数を返します。そして,この場合は,この制約がその端に少なくとも1つのインスタンスがなければならないことを指定しています。UMLの多重度定義を使用して,インスタンスの許容数を規定することもできます。上のルールを記述する場合は,現在多重度がアスタリスク(*),すなわち0以上となっているのを,1..*,すなわち1以上を示すように変更します。この場合,OCL制約は必須ではありません。
上の例の関連にロール名(orders)がないときは,代わりに小文字のクラス名(この例ではロール名とクラス名がよく似ているので注意)を,次のように使用します。
context Customer inv:
order->size > 1
図5.4に示すように,モデルで限定子つきの関連を使用しているときは,OCL式で特定のオブジェクトを参照するようにもできます。これは次のシンタックスで行われます。
context Customer inv:
orders[12589].totalCost() > 0
--注文番号12589は,totalCost > 0であること。
しかし,この例のように特定の注文番号を使うのは一般的でないことに注意してください。ルールは,特定のインスタンスに対してでなく,そのクラスのオブジェクトすべてに適用されるのが普通だからです。ここで言いたかったことは,限定子つきの関連を使用することで,コレクション中の特定のオブジェクトを識別する方法があるということです。
図5.4 ナビゲーションを伴う式はコレクションを返すことが多い
同じモデル中で,他のクラスに関連を使ってナビゲートするとき,その結果は,前節で例示したように,オブジェクトのコレクションとなることがよくあります。OCLには,より高度な操作を行うためのコレクション用の操作がたくさんあります。たとえば,コレクションのオブジェクト全体に対する反復,コレクションの中からある条件を満足するオブ
ジェクトのコレクションを部分集合として新たに作る,複数のコレクションを新しいコレクションに結合する,などです。コレクションのプロパティや操作は,矢のシンタックスで参照されます(たとえば,collection->property/operation)。これと,1つのオブジェクトのプロパティまたは操作に対する参照とは異なることに注意してください。こちらに対しては代わりにドットを使用します(たとえば,object.property/operation)。
コレクションに適用できる操作の,共通で簡単な例を示します(文脈はCustomerとします)。
self.orders->size -- 関連オブジェクトの個数を返す。
self.orders->isEmpty -- 関連オブジェクトがないときに真を返す。
ロール名は,関連を参照しオブジェクトのコレクションを評価するために使われます。前述したように,ロール名が定義されていないときは,小文字の関連クラス名を代わりに使用できます。
コレクションは,事前条件や事後条件を定義するときにも使えます。
context Order::addArticle(a : Article) : void
pre: a.number <> 0
post: order_articles = order_articles@pre->including(a)
post: sum = sum@pre + a.price
上の式では,引数の品物の品目番号が0であってはならないという事前条件が指定されています。そして,この操作の後には,order_articlesコレクションはその品物オブジェクトが含まれ,その値段の分だけ合計額が増えていることを指定しています。pre:とpost:ラベルは,操作の事前条件と事後条件を指定していることに注意してください。
OCLには事後条件を指定するときだけに使用できる2つの特別なキーワード,@preとresultがあります。resultキーワードは,操作の戻り値を示すためだけに使われてきました(上の式は,値を返さないのでresultキーワードは使われていません)。@preキーワードは,操作が実行される前の属性または関連の値を示すのに使用でき,同じ属性または関連が式の等号の両辺にあるときに使用できます。上の例では,1つ目の事後条件に使われていて,order_articlesコレクションは,操作後に引数の品物が含まれることを除いては操作前と同じであることを示します。
OCLにはモデルの型(クラス)に対するプロパティもあります。たとえば,nameプロパティは型の名前を返し,attributesプロパティは属性名のコレクションを返し,operationsプロパティは操作名のコレクションを返します。次の不変表明は,常に真と評価されるでしょう。
context Order inv:
Order.name = 'Order'
実際には,このような不変表明は指定されないでしょう。より実際的な例として,そのクラスに存在するすべてのオブジェクトを含むコレクションを返すallInstancesプロパティがあります。Orderクラスには全注文の総額を返すクラス操作(totalOrderSum)がありますが,これを利用します。
context Order::totalOrderSum( ) : Real
post: result = Order.allInstances->collect(sum)->sum( )
allInstancesプロパティはコレクション演算子collectが適用されるコレクションを返します。この演算子は,本章の後で述べますが,sum属性だけから成る新しいコレクションを作成します。最後のsum演算子はこのコレクションに適用され,全注文を合計します(Orderクラス中のsum属性とsum演算子を混同しないでください。それらはまったくの別物です)。AllInstancesを使用するのが危険なこともあります。それは,あるクラスの全オブジェクトの存在を大域的な文脈の中で(システム中の全Orderオブジェクト,あるいは世界中の全Orderオブジェクトというふうに)考えなければならないからです。操作中にそうした大域的な文脈になるシステムを定義するのが合理的だとしても,OCLはそのような大域的な文脈を定義しません。また,allInstancesをIntegerなどのように事前定義の型に適用すると,無限集合になってしまいます。
コレクションには3つの具体的な型があります。すなわち,Bag,Set,Sequenceです。これらはともに抽象的なCollection型のサブクラスです。Collection型はこれらに共通な操作を持っています。コレクション間の関係を図5.5に示します。Bagは要素の重複を含むことができる順序なしのコレクションです。Setは重複要素を含むことができない順序なしのコレクションです。Sequenceは重複した要素を含むことができる順序つきコレクションです。UMLダイヤグラムにおいては,特に指定されない限り,通常の関連はSetであると評価され,順序つき関連はSequenceであると評価されます。表5.2に,各型がサポートする
図5.5 OCLのCollection型の階層
操作を要約しています。抽象のCollection型に記載されている操作は,すべてのコレクションに有効であることに注意してください。また,どの操作もコレクションの内容を変更しないことにも注意してください。また,appendのように要素が付け加えられた新しいコレクションを返す演算子も,元のコレクションを変更しません。
コレクションに対するきわめて強力な操作が使えます。次に,最も一般的な操作(iterate, collect, select, reject, forAll, exists)について述べます。
iterate操作はOCLにおける反復のための一般的な演算子です。この操作は,コレクシ
ョン中の全要素わたって繰り返し行われ,そのコレクションの要素にOCL式を個々に適用します。iterate式には,最初にその式で使用されるiteratorの仕様,次に必要に応じてaccumulatorの宣言,最後に各要素に適用したりaccumulatorに代入したりする式が含まれます。iterate操作の一般的なシンタックスは次のとおりです。
collection->iterate(i : <IteratorType>;
acc : <AccType> = <initialexpr>
| <expr-with-i-and-acc>)
ここで,iteratorの型はそのコレクションと同じ型であり,accumulatorの型は任意の型とします。ordered_articlesの全合計を計算する反復は次のようになります。
context Order inv:
sum = ordered_articles->iterate(a : Article; result : Real = 0
| result + a.price)
この反復は,ordered_articlesコレクションの各要素の値をiteratorに割り当てます。次に,式result + a.priceを評価して,結果をresultというaccumulatorに代入します。これは合計額を累積します。式全体の型と値は,それぞれReal型と価格の合計額となります。
iterate演算子の代替手段はcollect演算子です。これは元のコレクションから新しいコレクションを作成します。この新しいコレクションの型は引数の式で指定されます。次のcollect演算子は,価格だけを持ったReal型のコレクションを作成するのに使用されていま す。sum演算子はこの新しいコレクションに適用されて,価格を合計します。
表5.2 Collection型と演算子
型 |
演算子と結果の型 |
演算子の説明 |
Collection |
size
-- Integer |
要素数 |
|
count(Object)
-- Integer |
Collection中のObjectの要素数 |
|
includes(Object)
-- Boolean |
Objectが含まれていれば真 |
|
includesAll(ObjColl)
-- Boolean |
ObjColl中のObjectがすべて含まれていれば真 |
|
isEmpty
-- Boolean |
Collectionが空なら真 |
|
notEmpty
-- Boolean |
Collectionが空でなければ真 |
|
sum(
) -- Integer or Real |
Collectionの全要素の合計 |
|
=
-- Boolean |
2つのCollectionが同一なら真 |
|
union(Collection)
-- Collection |
2つのCollectionの結合 |
|
Including(Object)
-- Collection |
Objectを加えたCollectionを返す |
|
Excluding(Object)
-- Collection |
Objectを除いたCollectionを返す |
|
Intersection(Collection)
-- Collection |
共通要素からなるCollectionを返す |
|
iterate(初期化式; 式)
-- 式の型 |
Collectionの全要素に対して式の実行を繰り返す |
|
select(式) -- Collection |
式 を満足する要素からなる部分集合を返す |
|
reject(式) -- Collection |
式 を満足しない要素からなる部分集合を返す |
|
collect(式) -- Collection |
式 の型を持つCollectionを返す |
|
exists(式) – Boolean |
式 を満足する要素が1つでもあれば真を返す |
|
forAll(式) -- Boolean |
全要素が式 を満足すれば真を返す |
Bag |
asSet(
) -- Set |
SetにしたCollectionを返す |
|
asSequence(
) -- Sequence |
SequenceにしたCollectionを返す |
Set |
s1
– s2 -- Set |
差(s1にあってs2にない要素)のSetを返す |
|
s1.symmetricDifference(s2)
-- Set |
どちらか一方にしかない要素のSetを返す |
|
asBag(
) -- Bag |
BagにしたCollectionを返す |
|
AsSequence(
) -- Sequence |
SequenceにしたCollectionを返す |
Sequence |
first
– Collection中の型 |
最初の要素 |
|
last
– Collection中の型 |
最後の要素 |
|
at(Integer)
– Collection中の型 |
Integerの位置にある要素 |
|
append(型) -- Sequence |
末尾に型 を追加したCollectionを返す |
|
prepend(型) -- Sequence |
先頭に型 を追加したCollectionを返す |
|
asBag(
) -- Bag |
BagにしたCollectionを返す |
|
asSet(
) -- Set |
SetにしたCollectionを返す |
context Order inv:
sum = ordered_articles->collect(price)->sum
select操作は,元のコレクションからその部分集合となる新しいコレクションを作成します。その結果の型は,元のコレクションと同じになります。この操作は,元のコレクションのオブジェクトのうちどれを新しいコレクションに含めるかを指定する式を持ちます。次の例は,全要素のうち,カテゴリ属性が「道具」であるtools_articlesと呼ぶコレクションを作成するためにselect演算子を使っています。
Context: Order inv:
tools_articles = ordered_articles->select(category = '道具')
reject操作はselect操作と同様の働きをします。ただし,式に一致するオブジェクトが取り除かれて新しいコレクションには含まれないところが異なります。forAll操作は,コレクションのすべての要素に対して式を評価します。この操作が真を返すためには,式は全要素について真でなければなりません。
次のforAll操作の例は,複数のオブジェクトを式の中で参照できることも示しています。これは2つのiterator,alおよびa2を使用して,コレクション中のオブジェクト全体について反復されます。そして,その式は,a1とa2が異なるならば,そのオブジェクトのnumber(品目番号)属性は異なっていなければならないと指定しています。これは,次に示されるように,number属性がすべてのArticleオブジェクトにおいてユニークでなければならないと指定することと同等です。
context Article inv:
Article.allInstances->forAll(a1, a2 : Article | a1 <> a2 implies
a1.number <> a2.number)
この他にも,isUniqueと呼ばれるコレクション用の機能があります。これを使えば,上の制約の記述がより簡単になります。
context Article inv:
Article.allInstances->isUnique(a : Article | a.number)
exists操作も,コレクション中の全オブジェクトについて式を評価しますが,操作が真を返すためには,このオブジェクトのうち少なくとも1つが式を満足していなければなりません。次の式は,カテゴリが「道具」である品物が,倉庫に少なくとも1つ存在するときに真を返します。
context Article inv:
Article.allInstances->exists(category = '道具' and availability =
#inStock)
OCLには副作用がないので,コレクションの演算子はコレクションの内容を変えることはありません。これらは,Boolean(真か偽)などの特定の値,あるいは新しいコレクションを返します。