汎化と関連を組合せてクラス図を書いた時,サブクラスのインスタンスの種類や個数の制約を定義したい


次のようなクラス図を書きたいとします。

  1. コントロールパネルはボタンを持つ
  2. ボタンの種類として,開始ボタンと終了ボタンがある
  3. コントロールパネルは,開始ボタンと終了ボタンを1つずつ持つ

みなさんならどのようにクラス図にしますか? 少し考えてみてください。

暫定解その1

1については,次のようにコントロールパネルがボタンをコンポジションとして持つという表現を用いたら良さそうです。

2については,次のようにボタンをスーパークラスに,開始ボタンと終了ボタンをサブクラスにした,汎化を用いたら良さそうです。

これらを組み合わせると次のような図になります。

しかしこれだと3を表現できていません。そのため,この図のままだと,たとえば「開始ボタンだけが複数存在し,終了ボタンが存在しないコントロールパネル」を許容してしまいます。

暫定解その2

3を表現することを考えましょう。シンプルに表現するには,次のように,コントロールパネルが開始ボタン,終了ボタンをコンポジションとして持ち,それぞれの多重度を1対1とすれば良さそうです。

ボタンがたとえば物理的なプッシュボタンのように,押すことの他には特に複雑な機能を有さない場合には,機能を再利用する必要性があまりないので,これがシンプルで良いかもしれません。

しかし,たとえばGUIで実現しているボタンだった時には,ボタンを部品として再利用したいという動機が生まれます。その場合には,このモデリングの方法ではうまくいきません。

暫定解その3

暫定解その1とその2を組み合わせることを考えます。

  • 暫定解その1より,コントロールパネルがコンポジションとしてボタンを持ち,ボタンのサブクラスとして開始ボタンと終了ボタンがあるというように表現します。
  • それに加えて,暫定解その2より,コントロールパネルが開始ボタンと終了ボタンをコンポジションとして持ち,それぞれの多重度を1対1にします。

こうすることで,1〜3の全てを一通り表現することができました。

しかし,コンポジションの線が複数張られていて,いかにも複雑です。

また,コントロールパネルからボタンへのコンポジションと,コントロールパネルから開始ボタン・終了ボタンへのコンポジションの間には,何も関係性を定義していません。なので,コントロールパネルから開始ボタン,終了ボタンにコンポジションが引かれているのと同じコンポジションが,コントロールパネルからボタンにも引かれているという保証がありません。もしかするとコンポジションが引かれていないかもしれませんし,全然関係ないボタンにコンポジションが引かれているかもしれません。こういう状況を「コントロールパネルからボタンへのコンポジションと,コントロールパネルから開始ボタン,終了ボタンへの関連の間に一貫性がない」というように言います。

模範解: OCLの利用

3のように種類や個数について限定するような記述のことを制約条件と言います。UMLでは制約条件を記述する有力な方法として,新たにOCL(Object constraint language: オブジェクト制約言語)というものを導入してみましょう。

これを用いた時にはクラス図そのものは暫定解その1の書き方と同じです。

違いは,次のようなOCLの記述を追加しているという点です。

context コントロールパネル inv:
  let startButton:開始ボタン = self.ボタン->select(b:開始ボタン | true) in
    let finishButton:終了ボタン = self.ボタン->select(b:終了ボタン | true) in
      (startButton->size() == 1) and (finishButton->size() == 1)

これの意味するところを日本語で書くと次のようになります。

  • startButtonをボタンで指されているオブジェクトの中でクラスが開始ボタンであるものを集めた集合とする
  • finishButtonをボタンで指されているオブジェクトの中でクラスが終了ボタンであるものを集めた集合とする
  • startButtonの要素数は必ず1であり,かつfinishButtonの要素数も必ず1である

つまり,開始ボタンと終了ボタンはコントロールパネルに1つずつしか存在しない,ということをOCLで定義しています。