 |
使えるUML 第10回 |
2007.11.1 掲載
「どのように(How)」から「何を(What)」へ
前回に引き続き今回の記事でも、OCL(Object Constraint Language : オブジェクト制約言語)の続きを行っていきます。今回は、『「どのように(How)」から「何を(What)」へ』と題しまして、一般的なプログラム言語とOCLの比較を行いながら問い合わせ操作の定義を説明します。プログラミングの経験のある方は、OCLを学ぶことが新しいプログラミング言語を押し売りされることとは少し違うということがお分かりいただけると思います。プログラミングの経験がない方には難しい内容に感じられるかもしれませんが、今回はご容赦ください。
◇問い合わせ操作を定義する
オブジェクトを検索して返す操作を「問い合わせ操作」といいます。問い合わせ操作では、オブジェクトの状態やリンクの変更を行いません。OCLは、この問い合わせ操作を定義することができます。ここでは演劇の予約システムを例にしながらOCLの問い合わせ定義に親しんでいきましょう。
以下は、クラス図です。
図1 演劇の予約システム
ある公演の予約者が他にどのような公演を好んで見ているのかを分析するために、公演クラスには「予約者が予約している別の公演のリスト」の問い合わせ操作が記述されています。以下はこの操作を定義するOCLです。
図2 予約者が予約している別の公演のリスト(順序なし)
| |
順序なし |
順序あり |
| 重複なし |
Set |
重複するオブジェクトを含まない。関連はデフォルトでSetとなる。 |
OrderdSet |
順序のつけれられたSet。 |
| 重複あり |
Bag |
重複したオブジェクトを含むが、順序はない。SetかBagに対して、さらにドットをつなげた関連の誘導はBagとなる。 |
Sequence |
重複を許し、順序を持つ。OrderdSetかSequenceに対してさらにドットをつなげた関連の誘導はSequenceとなる。 |
表1 コレクションの種類
それでは、OCL例のそれぞれの要素に注目しながら読み解いていきましょう。
- 定義する操作を表しています。また、返り値の型はSetです。表1にコレクションの種類を記述しましたので参考にしてください。
- 問い合わせ操作の定義を行うことを表しています。
- 演劇の予約の予約者(重複あり)を表しています。OCLでは「. <関連端ロール>」と記述することで関連するオブジェクトを誘導することができ、多重度が多の関連はデフォルトではSetとなります。予約の多重度は多なので「self.予約」は演劇に関連した予約オブジェクトのSetとなります。また、OCLではオブジェクト単体ではなく、オブジェクトのコレクションに対しても「. <関連端ロール>」を記述することができます。その場合、コレクションに含まれるオブジェクトのすべてに対して、関連端ロールを誘導した結果のコレクションを得ます。このときに結果の型は重複を許す型に変換されることに注意してください。「self.予約.予約者」は、Setのコレクションに対する誘導ですので顧客オブジェクトのBagとなり、重複した顧客が存在します。
- OCLはコレクションに対する多くの演算を標準で持っており、「-> <コレクション演算>()」と記述して使うことができます。「->asSet()」は重複のないSetにする演算で、(c)の重複した顧客のBagを重複のない顧客のSetにしています。ここまでで、公演のすべての公演予約者が手に入ります。
- その顧客の公演予約の公演です。「公演予約」までで予約のSetとなります。「公演予約.公演」だと公演のBagとなりますので、asSet()を行って公演の重複をなくします。
- (e)までで予約者が予約した公演のリストが手に入ります。しかし、この中には自分自身の公演も含まれています。自分自身の公演を削除するために、「->excluding(self)」を行います。
このようにして、予約者が予約している別の公演のリストが定義できました。しかし、返ってくる値はSetであるため、どの公演が予約者から好まれているのか順位づけすることができません。そこで、予約数の順番にソートしてみましょう。
図3 予約者が予約している別の公演のリスト(順序あり)
- 順序のあるSetを返すために、この操作をOrderdSetとして宣言します。
- 「self.予約.予約者->asSet()」を「公演予約者」として定義しています。OCLではこのように変数を作成することができます。変数が使用できるのは「in」の中だけです。
- 前述のOCLにsortedBy()以下の記述が追加されています。sortedBy()では引数の値の大小によってソートが行われます。返り値の型は演算対象の型によってOrderdSetかSequenceとなります。
- sortedBy()はイテレータ演算と呼ばる演算の一種で、コレクションの要素を繰り返し取り出しながら評価を行います。イテレータ演算の引数ではコレクションの要素を使用することができます。ここでの「予約」はsortedBy()の対象の、公演の予約となります。
- 「->intersection()」はコレクションの積集合(両方に含まれるオブジェクト)を返します。当該の演劇の予約で、かつ公演予約者の公演予約を取得します。
- sum()もイテレータ演算の一種で、コレクションの要素を繰り返し取り出しながら合計を取得します。
このようにして、予約数の順番にソートされた公演のリストを取得することができます。
OCLを使用してこれで目的のリストを取得することができました。はじめてOCLに触れる方は難しさを感じたかもしれません。
しかしそれでもプログラミングの経験のある方は、一般的な命令型プログラミング言語(CやJavaなど)でどのようにコーディングすることになるか、ここで少し時間をとってイメージしてみてください − 入れ子のForループや合計を保存するための変数などがでてきそうですが、きっとすぐにはアルゴリズム全体をイメージすることはできないでしょう。こうしたプログラミング言語が「どのように(How)」を記述するのに対して、OCLのような宣言的言語では「何を(What)」を記述するので、ほとんどの場合にシンプルに表現できます。問い合わせ操作を定義する場合には、プログラミング言語よりもOCLのような宣言的言語のほうが適しています。
◇OCL(宣言的言語)からプログラミング言語(命令型言語)へ
システム開発でも操作をOCLで記述できれば理想ですが、実際にはプログラミング言語で実装することが多いと思います。しかしそれでもOCLで記述することが無駄にはなるとはいえません。ここでは、OCL式のガイドにしたがってプログラミング言語へ変換していくことを見ていきましょう。
ところで、先ほどプログラミング言語では「どのように(How)」を記述すると説明しましたが、プログラミング言語でもコードのすべてがHowになるわけではありません。Howは操作の内部実装にあたりますが、操作の宣言自体は「何を(What)」になります。OCL式をいくつかの部分に分けて操作(What)として切り出し、それぞれの操作を実装していくことで、OCLでの宣言的な構造を保ったままプログラム言語へおとすことができます。
プログラミング言語での操作の実装(How)は避けることができないものですが、それでもより多くのWhat、つまり操作に分割してより宣言的に実装することは良いプラクティスとなります。
先の例のOCL式から操作を切り出してみましょう。
図4 操作の切り出し
上記の例のOCLから3つの操作を切り出しました。このようにして、OCLの式(What)から必要な操作(What)を、多少の修正は必要なものの比較的簡単に切り出すことができます。これらの切り出した操作を実装して、さらにそれらの操作を呼び出す実装を記述すればプログラム言語に落とすことができます。
また、このようにOCLにガイドされて得られたアルゴリズムはとても自然で美しいものになる傾向があります。
◇まとめ
今回は以下のことを学びました。
- 問い合わせ操作を定義しながらOCLの読み方に親しんだ。
- プログラミング言語が「どのように(How)」を記述するのに対して、OCLのような宣言的言語では「何を(What)」を記述する。問い合わせ操作にはOCLが適している。
- プログラミング言語においても操作を分割して宣言的に実装したほうが良い。
- OCLでプログラミングをガイドして、自然で美しいアルゴリズムを得ることができる。
今回はプログラミングの経験のない方には難しい内容だったと思います。また逆にプログラミングの経験のある方も宣言的言語の感覚は難しいものに感じられたかもしれません。違う言語で考えると違う答えが見つかるもので、言語が思考をガイドすることを肌で感じてもらえたなら嬉しく思います。ぜひOCLに親しんでプログラミングへも適用してみてください。
■筆者紹介 山下 智也/Tomonari Yamashita 株式会社テクノロジックアート テクニカルデプト UMLモデリングチーム
|