今回のテーマは「初級者にありがちな間違い」ということで、ここまでとは少し趣向を変えて、オブジェクト指向とUMLを習得しつつある人がしばらくの期間描きがちになる間違った例をいくつか紹介していきたいと思います。

その1:汎化関係に多重度

まずは、汎化(継承)の概念をいまひとつ理解しきれていない人が描くクラス図にたまに出てくる間違った例です(図1)。

図1:汎化関係に多重度が付けられている間違った例
図1:汎化関係に多重度が付けられている間違った例

この図はある図書館の貸出/返却処理や蔵書管理を行うシステムの分析モデルの一部と考えてください。UMLのクラス図を見慣れている人は、これを見てすぐに表記法上の間違いを指摘することができるでしょう。そう、汎化関係の両端に多重度が表記されているのがすごく変ですよね。このように描かれたクラス図を他の人から受け取ったとしたら、これらの多重度をどう解釈すれば良いのか悩んでしまいます。

UMLのクラス図ではいくつかの種類の関係線(依存、関連、実現、汎化、など)が使い分けられますが、これらの関係線のうち多重度(インスタンス同士の対応個数の可能性の範囲)を付ることができるのは関連線だけ(関連の特殊なものである集約とコンポジションを含む)です。他の関係線はクラス(型定義)同士の関係を表現するもので、これらの関係の上ではそれぞれのクラスのインスタンスがいくつ作られようが別に気にしません。汎化関係はこのようなクラス(型定義)間の関係の代表的なものです。

オブジェクト指向での汎化/継承関係のイメージをしっかり把握した上で、クラス(型定義)間の関係とインスタンス同士の参照関係をまとめた関連線とをちゃんと区別して扱わないと、この例のような間違った表記が出てきてしまうのかもしれません。

その2:不適切な汎化関係

図2:汎化関係の多重度を削った例 (表記的には正しい例)
図2:汎化関係の多重度を削った例 (表記的には正しい例)

図1のクラス図で汎化関係に付けられていた多重度表記を削った例を 図2に示します。このクラス図は表記的には正しい例になっているのですが、意味的にはどうでしょうか?

定義されている属性から推測するに、このクラス図中の「書誌情報」はこの図書館で取り扱っている個々の書籍のタイプ情報を表現する概念で、一方、「蔵書」はこの図書館に収蔵されている一冊一冊の本を表現する概念のように思われます。各々の「蔵書」インスタンスからは、その本のタイトルや著者やISBNなどの情報が参照できなければならないので、一見この例で示されているような汎化関係は良さそうに思えてしまうかもしれませんが、このクラス図の定義に従って生成されるインスタンスの構造例をオブジェクト図として描いてみると(図3)情報の重複が多くなっているということに気が付きます(特に同じ書籍の本が複数収蔵されている場合)。

図3:同じタイトルの3冊の別々の蔵書の例を示すオブジェクト図
図3:同じタイトルの3冊の別々の蔵書の例を示すオブジェクト図

クラス間の継承関係に関する原則として有名な リスコフの置換原則 (LSP: Liskov Substitution Principle) に照らして考えてみましょう。リスコフの置換原則を短い文章で説明するとしたら、だいたい以下のような内容になります:

スーパークラスのインスタンスが使われる箇所であればどこでも、そのスーパークラスのインスタンスの代わりにサブクラスのインスタンスを使っても(置き換えても)構造的にも意味的にも不整合が無いような場合にだけ継承関係を使いなさい。

この例の場合は「書誌情報」(スーパークラス)と「蔵書」(サブクラス)の関係に着目することになります。スーパークラスである「書誌情報」のインスタンスはある図書館で取り扱っている個々の書籍のタイプ情報を表わすような概念なので、使われ方としては、取り扱っている書籍の共通情報(個々の本で変わらない情報)の部分をカタログ的に登録して検索や管理に用いる...といったところでしょうか。では、そのカタログの一部に「書誌情報」ではなく「蔵書」のインスタンスが混ざっているような状況は問題無いか...と言われると、(「蔵書」のインスタンスは一冊一冊の本の実体に対する情報を表現するものなので)これは意味的におかしいように思えてきます。

このように考えていくと、どうやら『「蔵書」は「書誌情報」の一種』という関係(汎化関係)ではなくて、関連のあるふたつのクラスと捉えた方が良さそうです(図4)。

図4:「書誌情報」と「蔵書」の間を汎化関係と考えるのをやめた例
図4:「書誌情報」と「蔵書」の間を汎化関係と考えるのをやめた例

この例はモデリングの意味的な話に踏み込んでいるため、必ずしも「間違い」だとは言い切れない状況があるかもしれません。汎化(継承)関係はオブジェクト指向特有の仕組みで、「ここぞ」という所にビシっと使うと高い効果を得ることができます。反面、スーパークラスとサブクラスとの間の結合を必要以上に強めてしまったり、間違った使われ方をすると大きな混乱を招いてしまったりします。モデリング中に「あ、ここは汎化関係になるかな?」と思う箇所があったら、上述したリスコフの置換原則などを思い出して「本当に汎化関係にすべきか?」落ち着いて判断するようにしてください。

その3:関連線と属性との重複定義

もうひとつ、初めてクラス図を描く人が間違いがちなのは 図5のように関連線と属性との重複定義をしてしまうことです。

図5:関連線と属性とで重複定義されていそうな例
図5:関連線と属性とで重複定義されていそうな例

このクラス図では「書誌情報」と「蔵書」というふたつのクラス間に関連線が一本引かれているというとても単純な構造が示されています。ここでは「蔵書」クラスに「仕様:書誌情報」という属性が定義されていますが、これが、ある「蔵書」インスタンスから図中の関連線を辿って「仕様」という関連端名(ロール名)で参照できる「書誌情報」のインスタンスと同じモノを示しているのであれば、この属性の定義は不要です(既に関連線を引くことによって相手のインスタンスへの参照を持つことが示されているので、さらに属性として保持するように記述すると重複定義してしまっていることになります)。また、「書誌情報」クラスに定義されている「蔵書リスト:リスト」という属性もちょっと怪しそうな匂いがします。もし、この属性として保持されるリストが、ある「書誌情報」インスタンスから図中の関連線を辿って「実体」という関連端名(ロール名)で参照できる「蔵書」インスタンスの集合と同じモノを意図しているのであれば、この属性は不要です。この関連線を辿って参照できる「蔵書」インスタンスの集合とは別のインスタンス群を保持するために使われる...というのであれば、この属性は必要になります。このクラス図を描いた人がこの「蔵書リスト」という属性をどのような意図で使おうとしているのか確認しないと、この属性が単に間違いで重複定義されてしまっているものなのか、それとも本当に必要な属性なのか、判断することは難しいでしょう。

関連線でつながれたクラスには関連先の関連端名(ロール名)として相手のインスタンス(群)を参照することができる暗黙的な属性が定義されている...といった見方をします。たとえば、あるクラスに属性として定義する表記と、クラス間のコンポジションとして定義する表記とは、意味的にほぼ同じことを示しています(図6)。

図6:コンポジションと属性は意味的にほぼ同じ
図6:コンポジションと属性は意味的にほぼ同じ

また、属性定義には関連端に付けられるのと同じように多重度をカギ括弧([])で囲って表記することもできます(図7)。関連端の多重度と属性に付けられる多重度の関係、および、関連端名(ロール名)の前に付けられる可視性(Visibility)を示すマークと属性名の前に付けられる可視性マークとの関係などを確認してみてください。

図7:多重度表記を伴う属性定義の例
図7:多重度表記を伴う属性定義の例

おわりに

今回は「初級者にありがちな間違い」ということで、オブジェクト指向やUMLを習い始めたばかりの人達がまず引っかかってしまいがちな間違いをいくつか紹介してきました。これらの間違いの多くは、いろいろな書籍を読んだり、他の人が描いたモデルを見たり、あるいは、オブジェクト指向プログラミング言語に対する理解が進んでくるに従って、普通徐々に矯正されていきます。このような間違いは、継承や属性/関連の概念に対するイメージがまだ曖昧な状態であることに起因しているように思います。なので、このような間違いがモデル図中に見受けられたら、そのモデル図を描いた人がそれらの概念をきちんと把握しているかどうか、その場でモデル図を使いながら確認/補強していくと良いでしょう。