本連載のここまでの記事では、おもにUMLの表記法上の{誤解|誤り}について触れてきました。今回の記事では少し趣向を変えて、「表記法としては間違っていないけれども、表現している内容(モデルの意味)に{おかしな|あやしい}ところがある」というケースをいくつか見ていきたいと思います。今回の記事ではモデルの意味的な部分/解釈に関わってくることになるので、「明らかに間違っている」とは言えない(見方によっては十分に正しい)ケースも出てきます。そのため、この記事の内容が「どんなときも絶対的に正しい」などとは思わず、あくまでも参考程度に読んでみてください。

その1:エンティティとアクターの混同

図書館での本の貸出/返却業務を管理/支援するシステムを開発しているものと想像してみてください。主要な概念の構造を確認するために、まずは 図1のような概念モデル(クラス図)を作成してみました。このモデルはとてもシンプルで、概念モデルとして悪くない構造になっていると思います。

図1:図書館システムの概念モデル(クラス図)
図1:図書館システムの概念モデル(クラス図)

次に、図1のクラス図を前提として、「新規貸出」の正常系のシナリオ(『会員の「豆介さん」が"お手軽UML"という「蔵書」(管理ID=A012345)を新たに借りる』というシナリオ)について、概略のシーケンス図を描いてみました(図2)。

図2:「新規貸出」の正常系シナリオのシーケンス図
図2:「新規貸出」の正常系シナリオのシーケンス図

このシーケンス図を見て「なんだか気持ち悪いなぁ...」と感じる人は少なくないのではないでしょうか(「えっ!? どこが気持ち悪いの? シンプルですごくわかりやすいシーケンスですよね?」という人もいらっしゃると思いますが...)。ここでは、その「嫌な感じ」がどこから来るのか、その理由を少し考察してみましょう。

図2のシーケンス図の形を見てまず気になるのは、「会員」オブジェクトが他のいろいろなオブジェクトにメッセージを送って「頑張っちゃってる」ところでしょうか。このシーケンスのとおりにメッセージ送信を行うとしたら、これら4つのクラス間の依存関係は 図3のようになります。

図3:上記のシーケンスから読み取れるクラス間の依存関係
図3:上記のシーケンスから読み取れるクラス間の依存関係

この図は、『「会員」クラスは「図書館」クラスも「書誌情報」クラスも「蔵書」クラスも知っていなければならない』ということを示しています(結合度が高い)。つまり、他のアプリケーションでもっとも使い回されやすそうな「会員」クラスが分離しにくい形になってしまっています。

また、図2のシーケンス図では「会員」クラスのオブジェクトから新規貸出の一連の処理が始まっていますが、これは何か不思議な感じがしませんか? おそらく「新規の貸出を始めるのは会員だから...」という発想からこのようなメッセージの流れになっているように思われますが、本当にそうでしょうか?

良く考えてみると、図2ではエンティティとアクターを混同してしまっているように思えます。この例で示されている「会員」クラスは、本来、現実世界のひとりの会員に対する各種の情報を保持/表現するクラスで、「会員」という名前よりも「会員情報」というクラス名にした方が良い類の概念です(エンティティ)。イメージとしては「会員台帳」というバインダに綴じられた一枚の(会員ひとり分の情報が記入された)シートのような感じのモノを表しています。それに対して、新規貸出の処理を始めるのは、現実世界に実際にいるひとりの会員(アクターとしての会員)です。図2のシーケンス図では、「会員」というクラス名から「会員アクター」としての役割も持つものと{イメージ|勘違い}してしまったため、新規貸出の一連の処理が「会員」クラスから始まるような流れになっていますが、本当は「会員」クラスとは別にアクターとしての会員が別に登場するように考えるのが妥当でしょう(図4)。

図4:アクターとしての会員を追加したクラス図
図4:アクターとしての会員を追加したクラス図

図5には、アクターとしての会員を起点として考えた新規貸出(正常系シナリオ)のシーケンス図の例を示します。このシーケンスでは、「図書館」クラスが新規貸出の流れを制御する役割を担っていて(コントロール・クラス)、「会員」クラスはほぼ純粋に情報として扱われています(エンティティ・クラス)。

図5:アクターとしての会員を起点としたシーケンス図
図5:アクターとしての会員を起点としたシーケンス図

図6に、図5のシーケンスから読み取れるクラス間の依存関係を示します。図3の依存関係と見比べてみると、「会員」クラスの独立性が高まっているのがわかります。

図6:上記のシーケンスから読み取れるクラス間の依存関係
図6:上記のシーケンスから読み取れるクラス間の依存関係

その2:関連の意味付けとロールの混同/縮退

前出の図書館モデルの中から、「会員」クラスと「蔵書」クラスの間の関連に着目してみましょう(図7)。

図7:図書館モデルからの一部抜粋
図7:図書館モデルからの一部抜粋

ここで示されている関連には関連名、関連端名(ロール名)、および、関連の両端の多重度が明示されているので、これが「ある会員が今まさに借りている蔵書との間に張られる関係」であること、「ひとりの会員が同時に借りられる蔵書は最大5冊まで」であること、「ひとつの蔵書が同時に複数の会員に借りられることはない」ことなどが、ある程度正確に(誤解なく)読み取れます。

ところで、この関連がたとえば 図8のように描かれていたとしたらどうでしょうか?

図8:意味付けの解釈が曖昧になりそうな関連線
図8:意味付けの解釈が曖昧になりそうな関連線

このような描き方だと、このクラス図での関連線の解釈は一気に曖昧になってきます(ヒントが少なすぎていろいろな解釈が可能になっています)。この関連線の意味の解釈の仕方をぱっと思い付く範囲で並べてみましょう。

  1. ある「会員」が今まさにある「蔵書」を借りているということを示す関係 [現在形]
  2. ある「会員」が過去にどんな「蔵書」を借りたかということ(履歴)を示す関係 [過去形]
  3. ある「会員」がこれから借りようとしている「蔵書」(予約)を示す関係 [未来形]
  4. 上記 1、2、3 の組み合わせ。

私は、クラス図上に引かれる関連線の意味付けには「時制」のような{区別|特性}があると考えています。代表的なのは、上述したような、「今まさに~している」という関係を表わすために使われる「現在形」の関連、「過去に~していた」という履歴的な関係を表わすために使われる「過去形」の関連、そして、「近い将来~する予定である」という予約的な関係を表わすために使われる「未来形」の関連、などです。ところが、往々にして関連線の意味付けの一部を担う「時制」に関する前提が読む人にうまく伝わらないような描き方になってしまうことがあります。そのような場合は、そのクラス図を描いた人に意味付けを確認するようにしておいた方が良いでしょう。

さて、図8のクラス図を描いた人にこの関連線の意味付けを確認してみたところ、どうやら上記の1と2の両方の組み合わせとして解釈しているらしい...ということが判明したとします。しかし、そうすると、この関連線からでは 図7のクラス図では読み取れていた「ひとりの会員が同時に借りられる蔵書は最大5冊まで」であること、「ひとつの蔵書が同時に複数の会員に借りられることはない」こと、などの特性が履歴のための多重度に覆い隠されて見えなくなってしまっています。これらの特性が読み取れなくなっている理由は、現在形の関連と過去形の関連の両方を一本の関連線で{表現して|取り扱って}いるからでしょう(過去形の関連線の両端の多重度の方が制限が弱い(0..*)ので、現在形の多重度(0..1や0..5)がその中に埋もれてしまった)。

現在形の関連と過去形の関連を別々の関連線として区別して取り扱った場合のクラス図の例を 図9に示します。

図9:現在形と過去形の関連線を分けて考えたクラス図
図9:現在形と過去形の関連線を分けて考えたクラス図

※図中の「{sequence}」はその関連端で参照される要素群が「順番付けあり、重複あり」として取り扱われることを示しています(ここでは、同じ「会員」が同じ「蔵書」を別々の時期に複数回借りることがありうるので「要素の重複あり」として考えています)。

この例のように、ふたつの同じクラスどうしの間に引かれる(本来、役割や取り扱われ方が異なる)複数の関連が無理矢理一本の関連線にまとめられてしまっているために、その意味の解釈が曖昧になってしまったり、取り扱いが難しくなってしまっているようなクラス図をよく見かけます。そのような場合には、その関連で参照されるオブジェクト群の役割や関連の「時制」が混ざってしまっていないか考えてみるなどすると良いでしょう。

他の例として、「自動車」と「タイヤ」の関係を示します。まずは、個々のタイヤの役割を特に区別しない場合(図10)です。

図10:個々のタイヤの役割を特に区別しない場合
図10:個々のタイヤの役割を特に区別しない場合

次に、個々のタイヤの役割を明示的に区別して扱う場合のクラス図を示します(図11)。

図11:個々のタイヤの役割を区別して扱う場合
図11:個々のタイヤの役割を区別して扱う場合

これらはどちらが正解でどちらが間違いというわけではありません。そのモデルが利用される文脈でより有利な方の表現方法を都度選択して用いるべきでしょう。

ちなみに、「個々のタイヤの役割を区別して扱う時もあるし、4本のタイヤすべてに対して何か一律に行うこともある」というような場合は、本連載『第2回:隠れた曖昧さ』の最後の図で簡単に紹介した「派生ユニオン ( Derived Union )」を使って{表現して|取り扱って}みるのも良いかもしれません(図12)。

図12:派生ユニオンを使って表現した例
図12:派生ユニオンを使って表現した例

この形であれば、個々のタイヤを区別して扱う場合はそれぞれ「右前輪」「左前輪」「右後輪」「左後輪」という関連端名(ロール名)でタイヤを参照し、個々のタイヤを区別しない処理には「車輪群」という関連端名(ロール名)で4本のタイヤの集合として取り扱うということができます。

おわりに

今回の記事ではモデルの意味付けに関する話に立ち入ってきています。これは「何かのテーマについて書かれた作文に対して正しいか誤っているか論じる」ようなものなので、その解釈は(視点の取り方や表わそうとしている内容に応じて)いろいろとブレて正誤(または善し悪し)がスッキリと判断できない事が多くなります。

モデルの意味的な解釈(善し悪し)に関する判断は、実際にモデリングを行う際に避けて通れないところのひとつだと思いますが、その{説明|取り扱い}の難しさ故か、(表記法の正誤に比べて)モデルの意味的な{解釈|表現}に関するヒントやガイドラインを解説している記事はあまり多くありません。本記事が、そのようなヒント/ガイドラインのひとつとして何かの役に立つようになれば良いなぁ...と願っています。