コンポジションにまつわるアレコレ

誤解しがちなモデリングの技 第1回

皆川 誠

本記事は、2008年11月17日公開のものを再掲載しています。

はじめに

この連載では、コンサルティングでのモデル・レビューの場、教育講座実施中、各種の勉強会などの際に良く観察される、UML表記法やモデリングに関する典型的な誤解/勘違いをとりあげて解説を加えていきます。これによって、より正確なモデルの読み書き、効果的なモデル作成など、モデリング・スキルの向上を狙います。また、必要に応じて「あまり広く知られていないが、知っていると便利」なモデル要素や表記法についても随時紹介していこうと思います。

連載第1回のテーマは「コンポジション(Composition)」です。

コンポジションはクラス図でのクラス・アイコン間に引かれる関連線の一種で、インスタンス間の強い集約関係(全体-部分関係)を表現します(図1)。ここでは、コンポジションに関して良く観察される誤解や勘違いのいくつかを紹介していきます。

 

図1:コンポジションの利用例
図1:コンポジションの利用例

 

誤解/勘違い その1

コンポジションの全体インスタンスと部分インスタンスのライフサイクルは完全に一致する(全体クラスのインスタンスが生成される時に部分クラスのインスタンス群も同時に生成され、全体クラスのインスタンスが消滅する際には(コンポジションで所有されている)部分クラスのインスタンス群も必ず一緒に消滅する)。

 

【解説】

これは古いUML(バージョン1.3以前)の定義では正しい解釈だったのですが、UML 1.4 以降では『ある部分インスタンスの配置/廃棄/譲渡の責務は唯一その部分インスタンスを所有する全体インスタンスのみが担う』といった解釈に変更されていて、この部分の制約がずいぶん弱くなっています。

現在のコンポジションの定義であれば、部分インスタンスの動的な追加/削除/入れ替え(図2)や、全体インスタンスが破棄される際に部分インスタンスを他のオブジェクトに渡すことによって生き残らせる(図 3)ようなことも可能です。

 

図2:部分インスタンスの動的な追加/削除/入れ替えの例
図2:部分インスタンスの動的な追加/削除/入れ替えの例
図3:全体インスタンスが消滅しても部分インスタンスが生き残る例
図3:全体インスタンスが消滅しても部分インスタンスが生き残る例

 

ちなみに、UML 1.4 は2001年9月にリリースされています。上記ような新しいコンポジションの定義が公開されてからもう何年も経過しているのにもかかわらず、いまだに古い解釈の方で理解している人が意外と多いのは何故なのでしょうか? このあたりの定義が変わったという事実を教える側の人が知らず、古い解釈のまま若手の人に伝えてしまっているのかもしれません。あるいは、教える側の人が手抜きをしてしまっている(「ライフサイクルが完全に一致する」と言ってしまった方が教えるのが楽だから)・・・という状況もあるのかもしれません。

 

誤解/勘違い その2

コンポジションの全体側の多重度は必ず「1」でなければならない。

 

【解説】

そんなことはありません。コンポジションの場合、全体側の多重度は「たかだか1」まで(つまり「1」または「0..1」)しか取り得ません(※1)が、これが必ず「1」でなければならない・・・となると制約が強すぎてコンポジションの使いどころが随分限られてしまうことでしょう。このような勘違いがいったい{何処から|何故}広がってしまったのか不明なのですが、意外と多くの人が「1」にしなくちゃならない・・・という(強迫観念のような?)誤解をしていたりします。

※1:
これはコンポジションの共有不可制約(『ある部分クラスのインスタンスが、全体クラスの複数のインスタンスに同時にコンポジションで所有されることはない(共有されない)』という制約)に由来する制限です。もし、あるコンポジションの全体側多重度に「1..*」や「0..*」と記述されていたとしたら、それは「部分クラスのひとつのインスタンスが同時に全体クラスの複数のインスタンスに所有されることがある」という意味になるので、この時点でコンポジション自身が暗に持っている共有不可制約に反してしまう(矛盾してしまう)のです。

 

コンポジションの全体側の多重度に限らず、関連線の両端に付く多重度はインスタンス同士の存在依存に関する制約条件を示しています。コンポジションの全体側の端に付く多重度の場合は、あるひとつの部分インスタンスが存在するために満たさなくてはならない条件(その部分インスタンスが全体インスタンスに組み込まれている時だけ存在できるものか、それとも、全体インスタンスに組み込まれていなくても単独で存在できるものか)を示すことになります(図 4)。

図4:全体側の多重度が「1」の場合と「0..1」の場合の解釈の違い
図4:全体側の多重度が「1」の場合と「0..1」の場合の解釈の違い

 

誤解/勘違い その3

ある部分クラスが複数の全体クラスとコンポジションで結ばれることはない。

 

【解説】

これはコンポジションの共有不可制約を間違って解釈してしまったために起こる誤解/勘違いだと思われます。

共有不可制約はインスタンス間のリンクに対する制約なので、べつにあるクラス(=型の宣言)が複数の別の全体クラスの部分クラスとしてコンポジションで結ばれても問題ありません。たとえば、図5 の例では、「タイヤ」というクラスが「バイク」と「自動車」両方の部分クラスとしてコンポジションで結ばれています。これは、ある「タイヤ」のインスタンスが「バイク」のインスタンスと「自動車」のインスタンスで共有されるということを意味しているわけではなく、実際のインスタンス群の構造はそれぞれ独立した木構造になります(図6)。

図5:部分クラスが複数の全体クラスとコンポジションで結ばれる例
図5:部分クラスが複数の全体クラスとコンポジションで結ばれる例

 

図6:前述のクラス図に合致するオブジェクト図の例
図6:前述のクラス図に合致するオブジェクト図の例

 

上記のように、あるクラスが複数のクラスからコンポジションで結ばれる状況は良く使われますが、この際に特に注意しなくてはならないのはコンポジションの全体側の多重度です。たとえば、図5のクラス図が図7のように変更されたとしたら(コンポジションの全体側の多重度が「0..1」から「1」に変わっています)、このクラス図(型定義)ではもう「タイヤ」のインスタンスは存在できない構造になってしまいます。

図7:部分インスタンスが存在できない構造になってしまっている例
図7:部分インスタンスが存在できない構造になってしまっている例

 

図7のクラス図の多重度を正確に読み取ると、『「タイヤ」のインスタンスがひとつ存在するためには、その「タイヤ」のインスタンスを部品として保有している「バイク」のインスタンスが必ずひとつなければならず、かつ、おなじ「タイヤ」のインスタンスを部品として保有している「自動車」のインスタンスも必ずひとつなければならない。』ということになるのですが、これはコンポジションの共有不可制約にバッチリ違反してしまいます。そのため、このクラス図の構造では「タイヤ」のインスタンスはひとつも生成することができません。「タイヤ」のインスタンスが存在できないので、「バイク」のインスタンスも「自動車」のインスタンスも生成することができません。

このように、多重度のちょっとした違いが大きな制約の違いになってくることがあるので、関連線の多重度はよく考えて設定するように心がけましょう。安易にイイカゲンな多重度を付けてしまうと、自分の頭の中で考えている構造とは違った構造イメージを人に伝えてしまうことになったりします。