連載
【改訂版】初歩のUML
第7回
オブジェクトの動的側面を見極める
萩本順三 株式会社豆蔵
2003/6/5
今回は、前回の店舗と売り上げのモデルを例に、オブジェクトの動的側面を観察するためのモデリングについて説明していきます。
オブジェクト図 |
第6回「関連の理解をさらに深める」で説明した店舗と売り上げのクラス図(図1)の実際のインスタンス構造を理解するには、オブジェクト図が有効です。オブジェクト図は、複雑なクラス図を説明するために、インスタンスが動作する際のイメージ例を図として表現したようなものです。つまり、インスタンス同士が動作するときの重要なシーンを写真に撮ったようなものです。
|
では、図1のクラスから作られる実際のインスタンスの例を、オブジェクト図として表してみましょう。図2はオブジェクト図をフォーマルに書いたものです。
|
前回もオブジェクト図を使いましたが、ここではオブジェクト図のモデル要素についても、きちんと説明していきます。モデル要素とは、図の中で使用される図中のそれぞれのモデルの意味を表す図要素のことです。オブジェクト図では、「オブジェクト」「リンク」のほか、「コンポジション」「集約」「誘導可能性」など、クラス図で使用できるモデル要素を必要に応じて使うことができます。また、属性に属性値を入れたものも使います。ただし、多重度は使えません。「リンク」とは、オブジェクト図で使える線のことで、「リンクは関連のインスタンスである」といえます。オブジェクト図の利用ポイントとしては、クラス図によって生成される重要なオブジェクトのリンクの例を示すように使います。例えば、この図2は、当日の売り上げを示しています。また、売り上げにはいくつかの明細があり、各明細は、商品とリンクしています。明細は商品を共有することもあるという重要なポイントを、この図では示しています。
シーケンス図 |
次は、部門クラスの操作(売上合計)を呼び出してみることで、クラス図の検証をしてみましょう。果たして、このモデルは、正しく動作できるのでしょうか。これを確認するにはシーケンス図を使います。UMLの表記法の中で、シーケンス図は、オブジェクト同士の動的側面をとらえるための相互作用図の1つとして知られています。相互作用図には、シーケンス図とコラボレーション図があります。コラボレーション図についても今回説明します。
ではさっそく、シーケンス図の作成方法について説明します。
シーケンス図は、まずオブジェクト図の中で操作シーケンスを説明するうえで、最も適切なオブジェクトをいくつか代表として列挙します(図3)。
|
次に、店舗から部門の売上合計操作メッセージを呼び出したときの流れを矢印で書いていけばシーケンス図の出来上がりです(図4)。
|
シーケンス図のモデル要素 |
さて、ここからは、シーケンス図で使用できるモデル要素について詳しく説明します。まずは、図5をご覧下さい。
|
1から5まで、シーケンスのモデル要素を示す番号がふってあります。以下、順番にモデル要素の説明をしていきます。図5を参照しながら、以下の説明をお読みください。
(1)ライフサイクル
ライフサイクルは、オブジェクトが作成されてから消滅するまでの区間を点線を使って書きます。もし、オブジェクトを途中で生成(オブジェクトにメッセージの線を直接書く)し、途中で消滅(×マーク)させる場合は図6のように、明示的に表現することができます。
|
(2)活性期間
活性期間は、操作が実行中または操作の中からほかのオブジェクトの操作の呼び出し中の期間を示します。シーケンス図の中でやや分かりづらい概念です。オブジェクトのライフサイクルと混同しないよう注意してください。
(3)メッセージ
オブジェクトのメッセージは矢印で記入します。例えば「売上合計()」というメッセージを「衣服:部門」オブジェクトに送るということは、部門クラスに「売上合計()」という操作があることが前提となります。また、このようにメッセージを送るには、送信元と送信先のオブジェクトのクラス同士に何らかの関係(関連、集約、コンポジション、依存)がある必要があります。
メッセージを呼び出した後、活性期間が切れているところでメッセージがリターンしていると考えましょう。このことを明示するために、点線矢印でメッセージの戻りを書くことがありますが、一般的にメッセージの戻りは省略されます。
|
メッセージには、下記のように非同期メッセージ、同期メッセージがあります。非同期メッセージとは、並行処理を進めるためのものです。
|
(4)メッセージの自己呼び出し
自分自身のオブジェクトのメッセージを呼び出す際は、図5のように、メッセージを自分自身に呼び戻すような矢印を用いて記述します。
(5)オブジェクト
オブジェクト図で何度も使ってきたオブジェクトの表記です。オブジェクトのタイプとして、複数存在することを示すためのマルチオブジェクトや自分自身が並行動作できる能力を持つことを示すアクティブオブジェクトといったものがあります。
図4のシーケンス中の衣服部門の配下には、複数のサブ部門がありますが、マルチオブジェクトを使えば図9のように表現することができます。
|
アクティブオブジェクトは、他の制御メッセージと並行的に制御できるメッセージを発するオブジェクトの事です。アクティブオブジェクトは、並行処理を行う際に非同期メッセージと一緒に使用するとよいでしょう。アクティブオブジェクトは、オブジェクトの枠を太線で囲みます。アクティブオブジェクトの例とその説明については、図10で行います。筆者はHORB ver2.xのアーキテクチャを設計した際、アーキテクチャドキュメントのさまざまな並行処理の部分の説明にシーケンス図を使いました。実際、UMLのシーケンス図で並行処理を表現しようとすると、かゆいところに手が届かないといった感じがします。このドキュメントは、http://horb.a02.aist.go.jp/horb-j/で「HORB 2.1 コア・アーキテクチャドキュメント」として公開されていますので、興味がある方は、ダウンロードして参考にしてください。UMLクラス図、シーケンス図、オブジェクト図などの利用例が盛りだくさんです。
並行処理におけるシンプルなシーケンス図の例 |
以上のモデル要素を活用して、並行処理におけるシンプルなシーケンス図の例を示してみましょう。Javaではおなじみのjava.lang.Runnableを実装したオブジェクトによる並行処理について書いたものを図10に示します(Runnableは、第5回「クラスの依存関係と実現関係」で取り上げました)。図10は、下記のようなストーリーをシーケンス図で表したものです。
ストーリー |
java.lang.Threadのコンストラクタの引数に、Runnableを実装したオブジェクトを渡して、Threadインスタンスを生成し、そのインスタンスのstartメッセージを呼び出すと、JVMによって新たなスレッドコンテキストが生成され、そのコンテキスト上からRunnableを実装したオブジェクトのrunメソッドが呼ばれます。 |
|
いかがでしょうか。この図から2つのスレッドが同時に走っているのが分かりますか? この図は、アクティブオブジェクトを2つ(MyClassとJVM)登場させています。最初のスレッド(MyClass)が動作している最中に、もう1つのスレッド(JVM)が動き始めることをアクティブオブジェクトを使って表現しているものです。また、2つめのスレッドは、並行処理を行うことになりますので、JVMからJava.lang.Threadに伸びるメッセージの線は非同期メッセージにしています(注)。
少々分かりづらいですね。でも、この図には私なりに、上記UMLの標準的な表記に2つの工夫を追加しています。
まず1つ目は、苦しまぎれですがJVMを登場させることで、OSのスレッドが振り出され、そのコンテキスト上からThreadインスタンスのrunが呼び出されていることを説明しています。どの時点でOSからスレッド(実行単位)が切り出され、並行制御が行われるのかをJVMをアクティブオブジェクトとして登場させることで説明しています。
皆さんもご存じのように、そもそもオブジェクトとOSの資源であるスレッドは直交した概念ですので、アクティブオブジェクトのように、オブジェクトという単位にスレッドが割り付けられるというのも不自然な話なのです。そこで、この図の2つ目の工夫として、メッセージの線をスレッド別に分けています。こうすることで、どちらのスレッドがどのメッセージを呼んでいるかが分かります。また、もしスレッド同士がwait、notifyなどで同期を取る場合、ノートを使ってコメントを書き加えるようにします。
(注)UMLはKava.lang.Threadのようにパッケージを含めたクラス名の場合、パス名と呼ばれる表記を使います。パス名は、図10の「java::lang::Thread」のように「::」をパス間、またはパスとクラスのセパレータ文字として使用します。これはオブジェクト表記、クラス表記共通の記述です。
コラボレーション図 |
コラボレーション図は、シーケンス図と同様、相互作用図の1つです。シーケンス図と同じようにオブジェクトの相互作用を表していることに違いはありません。しかしコラボレーション図は、オブジェクトとオブジェクトのリンクイメージを残します。図4のシーケンス図をコラボレーション図で表現したものを図11に示します。
|
クラス図の詳細化 |
シーケンス図によって、それぞれのクラスの操作の割り付けが詳細化できました。例えば、明細の小計は派生属性にしていましたが、操作として定義することにしました。売り上げには税額計算操作が追加されました。商品の金額取得操作は省略されています。このように、属性のset()、get()についてはクラス図が煩雑になるために省略します。
このようにシーケンス図やコラボレーション図は、クラスから生成されるオブジェクトの相互作用面を考察することで、最終的にクラス図の詳細化、洗練化につなげていくために使われます。ただし、シーケンス図やコラボレーション図は、この連載で紹介した図のようにきれいに清書することだけが正しい利用方法ではありません。ホワイトボードでのディスカッションにて、これらの図を描くだけで特に清書しないこともあります。特にシステム分析段階などではそのような使われ方をすることが多いでしょう。
|
(コラム)オブジェクトの世界はソフトウェアの都合で作られた恣意的な世界 | ||
今回ご紹介した店舗と売り上げのモデルでは、明細は商品を共有するという構造を持っています。これは、現実の世界の明細と商品の構造とは異なります。なぜなら、現実の世界の商品、例えばコーラを2つ買うと、2つの商品(コーラ)オブジェクトが使われます。よって、明細と商品の関係は1対1なのです。なぜ、明細は商品を共有するようにしたのでしょうか。われわれは、それを当然と思っているのでしょうか。それは結局、皆さんが、ソフトウェア開発の常識であるデータの重複排除、つまりデータは一元管理すべきという原則を知っているからだと私は思います。明細に現れる商品(コーラ)の情報は、1つのオブジェクトで管理させるべきというソフトウェアエンジニアリングの常識を皆さんが知っているからこそ、自然に思えているだけなのです。 オブジェクト指向は、現実の世界をそのまま描写しているように誤解する方が多いのですが、実はそうではありません。ソフトウェアという仮想世界を現実世界に似せて表現することで、仮想世界のもやもやした構造を、人が理解しやすくしているだけなのです。ですから、実際のオブジェクトは、ソフトウェア開発にとって都合が良い構造に変化させる必要があり、非常に恣意的なものと考えるべきなのです。そうしないと無意味な分析、設計に時間をかけてしまうといったオブジェクト指向の落とし穴にはまりこんでしまいます。 現実世界の「もの」の名前を借用して、オブジェクトに名付けてあげることで、みんながそのオブジェクトの存在を理解するということがオブジェクト指向のメリットなのです。そしてそのようなテクニックが、オブジェクト指向分析、設計、実装のそれぞれの局面において重要なものとなるのです。
また、第4回「関連クラスと集約、コンポジション」でも述べましたが、ソフトウェアで、「何をやりたいのか」がクラスの構造やクラスの関係構造を決定します。何をやりたいのかが決まらなければ、そもそもクラスの仕様が正しいかどうかさえ分からないのです。先に挙げた、明細と商品の関係についても、すべての取引で同じ構造を持つかというと、そうではありません。例えば、レンタルビデオショップの場合、明細と商品は1対1になるかもしれません。なぜなら、売り上げの明細につながる商品はビデオ本体となり、ビデオが1つ貸し出されると在庫が1つ少なくなることを管理しなければならないからです。つまり在庫管理までも対象としている場合、構造が変わってきます。これも、やりたいことが変わるとクラスの関係構造が変わるという例ですね。 |
今回はここまでです。次回は、「やりたいことは何か」「やるべきことは何か」に焦点を絞ったモデリングを紹介します。そうです。ようやくユースケースモデルの登場です。
IT Architect 連載記事一覧 |