ObjectSquare [2003 年 4 月号]

[技術講座]


Java ではじめる UML

[第5回] UML によるシステム分析

山内亨和/高木栄児/山口健/伊藤喜一/林俊樹/森三貴

  前回までで UML の主要なダイアグラムについての説明が終わりました。 今回と次回でケース・スタディの題材であるスケジューラ・ソフトにアラーム機能の追加を行います。 今回はユースケースを用いたシステムの機能分析のしかたと、分析時にクラス図を使うテクニックについて解説します。 本稿を読み終えた頃には UML が問題 ( システム開発 ) を把握するために有効であることがお分かりいただけるかと思います。


1. 前回のおさらい

 まず始めに、前回の内容を簡単に振り返っておきましょう。
 前回は、 Java で開発されたスケジューラ・ソフトの日付セルの状態とその遷移を整理するためにステートチャート図を使い、スケジューラの起動から終了までの処理の流れを整理するためにアクティビティ図を使いました。 前回の解説で、ステートチャート図を使うとシステムの構成要素の状態遷移を整理でき、アクティビティ図を使うと処理の流れを整理できることをおわかりいただけたかと思います。
 さて、今回から既存のスケジューラ・ソフトにアラーム機能を追加することになります。 とはいうもののまだアラーム機能の詳細な仕様が決まっていないため、すぐに設計を始めるわけにはいきません。 そのため、 Chen 君はアラーム機能の要件定義を始めるようです。
 スケジューラ・ソフトには既に以下のような機能が備わっています。

		
  ・カレンダー画面を表示する
  ・予定を入力する
  ・予定を編集する
  ・予定を削除する
  ・1 日の予定一覧を表示する
		

 Jun 先輩から追加依頼されたアラーム機能についての記述は以下のものです。

			
  予定の時間になったら知らせてくれるアラーム機能
			
Chen 君の発言
「これだけの記述だとどんなプログラムを作ればいいかわからないな。アラーム機能をもう少し詳しく書き直してみよう。」

			
  予定の時間にポップアップ・ウィンドゥ ( 画面 1 ) でアラーム表示する。    
			
画面 1 : アラーム表示画面


2. ユースケース分析

 UML は機能要件を定義するためのダイアグラムとしてユースケース図を提供しています。 ただし、ユースケース図は機能要件を定義するのに十分な記述力がないため、ユースケース図と一緒に、ユースケース記述というドキュメントを作成することが一般的です。

2.1 ユースケースの表記法

 それでは、スケジューラ・ソフトの各機能とその動作を基に、実際にユースケース図を描いてみましょう。 まずは、既存機能の「カレンダー画面を表示する」を例にとり、ユースケース図の基本的な表記法について説明します。 ユースケース図を作成する場合にはまず始めに、「システムの機能の利用者は誰か」、「システムの機能を起動するものは何か」を考えます。
 「カレンダー画面を表示する」機能の利用者、起動するものはスケジューラ・ソフトのユーザーです。 この利用者、起動するもののことを「アクター ( Actor ) 」といいます。 アクターは図 1 のような人形のアイコンで表現します。 アクターはクラスと同じようにアクター間で「関連」や「汎化」を記述することができます。


アクタとユースケースの関連
図 1 : アクター

 「ユースケース ( UseCase ) 」とはシステムが外部に提供している機能を示す用語です。 今回の例では「カレンダー画面を表示する」機能がユースケースになります。 UML ではユースケースを図 2 のような楕円で表現します。 また、ユーザーアクターが「カレンダー画面を表示する」ユースケースを使用するという関係を表現するためには、図 3 のようにアクターとユースケース間に「関連 ( Association ) 」を引きます。


ユースケース アクタとユースケースの関連
図 2 : ユースケース 図 3 : アクタとユースケースの関連

 ユースケースは他のユースケースを使用することができます。 現行のスケジューラ・ソフトでは「カレンダー画面を表示する」ユースケースの日付セルを表示するために、 DateCellRenderer 、 DateCell 、 CalendarDate 、 Scheduler クラスが図 4 のように協調動作しています。


日付セルの表示に関するシーケンス図
図 4 : 日付セルの表示に関するシーケンス図

DateCellRenderer 、 DateCell クラスは「カレンダー画面を表示する」ためのプログラムですが、 CalendarDate 、 Schedulerクラス は「予定情報を管理する」ためのプログラムです。 「予定情報を管理する」機能を 1 つのユースケースと切り出した場合には、「カレンダー画面を表示する」ユースケースは「予定情報を管理する」ユースケースを「包含 ( Include ) 」しているといいます。 包含を表現するために、図 5 のように包含するユースケースから包含されるユースケースに対して破線矢印を描き、その矢印にラベルとしてステレオタイプ << include >> を描きます。 この関係を包含関係といいます。


include関係
図 5 : include関係

 また、ユースケースは他のユースケースで拡張することができます。 「カレンダー画面を表示する」ユースケースでは日付セルを表示する際に、その日付の予定がある場合には黄色、予定がない場合は白でセルを表示します。 つまり、「カレンダー画面を表示する」ユースケースは日付に対応する予定がある場合には「日付セルを黄色表示する」ように拡張しています。 このような場合、「カレンダー画面を表示する」ユースケースを「日付セルを黄色表示する」ユースケースで「拡張 ( Extend ) 」するといいます。拡張を表現するために、図 6 のように拡張内容を描いたユースケースから拡張するユースケースに対して破線矢印を描き、その矢印にラベルとしてステレオタイプ << extend >> を描きます。 この関係を拡張関係といいます。


extend関係
図 6 : extend関係

 拡張関係にはオプションとして拡張が実行される条件を以下のような書式でラベルをつけることができます。

			
  [ 条件文 ] 
   		 	

  拡張するユースケースには拡張ユースケースを実行するタイミングを「拡張点 ( ExtensionPoint ) 」として記述することができます。 拡張点は図 6 の「カレンダー画面を表示する」ユースケースのように、拡張するユースケースを水平線で分割した下の区画に、以下のような書式で記述することができます。

			
  略称 : タイミング 
			

 さて、それではスケジューラ・ソフトに新しく追加するアラーム機能のユースケースについて考えてみましょう。 前述の機能名称から、ユースケース名を「予定の時間にアラーム表示する」としましょう。
 それでは、このユースケースのアクターは何になるでしょう。 前にも挙げましたが、アクターとはシステムの機能の利用者、システムの機能を起動するものです。 機能の利用者はユーザーですが、予定の時間になったときにアラーム表示をするのはーではありません。 ユースケース図を記述する際に、ユースケースの起動をするものが、システムのーとは別なことはよくあります。 今回のような、特定の時間に起動されるプログラムや、バッチプログラムなどがそうです。 このようなケースでは、システムを起動するものとして、タイマーやジョブスケジューラなどをアクターとして定義するとよいでしょう。 アクターにはシステムを使う『人』が対象になるのではなく、開発対象のシステムとコミュニケーションをとる『システム外の全てのもの』が対象になります。 システムが別のシステムにアクセスする場合にも、別システムはアクターとなります。 今回のユースケースではシステムを起動するアクターとしてタイマーを定義します。
 「予定の時間にアラーム表示する」ユースケースを起動するのはタイマーですが、このアラーム機能を利用するのはスケジューラ・ソフトのユーザーです。 ユーザーはアラーム表示のポップアップ・ウィンドウを確認するため、「予定の時間にアラーム表示する」ユースケースとコミュニケーションをもちます。 「予定の時間にアラーム表示をする」ユースケースとタイマーアクター、ユーザーアクターの関係をユースケース図で表現すると図 7 のようになります。


図7:アラーム機能のユースケース図
図 7 : アラーム機能のユースケース図

2.2 ユースケース記述

 ユースケース図は「ユースケースとして何があるか」、「システムの利用者、起動するものとして何があるか」を定義することに適していますが、「ユースケースが具体的に何をするか」を定義するには適していません。 ユースケース図のこの不足をユースケース記述というドキュメントで補うことになります。 ユースケース記述は図ではなく文章で構成されています。 ユースケース記述は UML ではありませんが、ユースケース図と同時に書く重要なドキュメントなので、本稿でも説明します。
 ユースケース記述では以下のものを書きます。

			
  ・ユースケース名
  ・アクター名
  ・事前条件
  ・事後条件
  ・基本フロー
  ・代替フロー
			

 それでは「予定の時間にアラーム表示する」ユースケースのユースケース記述を書いてみましょう。
 「予定の時間にアラーム表示する」ユースケースのユースケース記述では、ユースケース名は「予定の時間にアラーム表示する」になり、アクター名は「タイマー」になります。
 事前条件とは、ユースケース実行前に満たされていなければならない条件です。 「予定の時間にアラーム表示する」ユースケースの事前条件は以下のようになります。

事前条件
			
    
  スケジューラ・ソフトが起動されている。
			

 事後条件とは、ユースケース実行後に満たされていなければならない条件です。 「予定の時間にアラーム表示する」ユースケースの事後条件は以下のようになります。

事後条件
			

  現在時刻の予定がある場合に、ユーザーがアラーム表示を確認できる。    
			

 基本フローとはユースケースが成功裏に終わる場合の、処理の流れを文章で書いたものです。 基本フローは処理の流れを箇条書きに書きます。 「予定の時間にアラーム表示する」ユースケースの基本フローは以下のようになります。

基本フロー
			
  1. タイマーが、「予定の時間にアラーム表示する」ユースケースを起動する。    
  2. システムは、スケジューラに現在時刻の予定があることを確認する。    
  3. システムは、現在時刻に登録されている予定をアラーム表示する。    
  4. ユーザーは、アラーム表示を確認する。    
			

 基本フローは始めにアクターによってユースケースが起動される様子を書きます。 以降はアクター、もしくはシステムを主語に使い処理の流れを書きます。 ここでのコツは処理の流れを詳しく書きすぎないことです。 詳しい内容は得てして実装の内容を多分に含むものであったり、基本フローのような箇条書きの形式で書くのに向かない内容であったりします。 基本フローに書くのに向かない要件の記述は、ビジネスルール定義書のような自由形式のドキュメントにまとめるとよいでしょう。
 代替フローとはユースケースが成功裏に終わる基本ケース以外の処理の流れや、失敗に終わるケースの処理の流れを文章で書いたものです。 代替フローは基本フローや他の代替フローの参照から始まり、以降処理の流れを箇条書きに書きます。 「予定の時間にアラーム表示する」ユースケースの代替フローは以下のようになります。

代替フロー
			
  基本フローの 2 でスケジューラに現在時刻の予定がないことを確認した。    
  1. 何もせずに本ユースケースを終了する。
			

 これで、「予定の時間にアラーム表示する」ユースケースのユースケース記述を書き終わりました。


3. UMLによるシステム分析

Chen 君の発言
「既にある機能と新しいアラーム機能の概要については、ユースケース分析で大体つかめたな。 次に現行のスケジューラ・ソフトがどんな要素で構成されているかとアラーム機能を追加することで何を追加しなければならないか、クラス図とシーケンス図で分析してみよう。」

3.1 既存システムのモデル化

 今回の開発は、現行のスケジューラ・ソフトへの機能追加になります。 そのため、スケジューラ・ソフトのクラス、どのメソッドに変更を加えるか考えなければなりません。
 このような場合には、現行のシステムを、システムの目的を端的に表した単純なモデルに変換して、そのモデルに何を追加すればアラーム機能が実現できるか考えると開発がスムーズに進むでしょう。 まずは、現行のシステムを単純なモデルに再構成してみましょう。
 「カレンダー画面を表示する」ユースケースの日付セル表示を実現しているクラスのシーケンス図は前述の図 4 の通りです。 このユースケースに登場するクラスを大別すると以下のようになります。

画面を表示するクラス DateCellRenderer
DateCell 画面を表示する情報を加工するクラス ScheduleList
Scheduler 予定情報を管理するクラス ScheduleList

 クラス図を使った分析をする際に頻繁に使用するステレオタイプとして << boundary >> 、 << control >> 、 << entity >> があります。 boundary ( 以下、バウンダリ ) はシステムが外部のアクターと対話するために必要なクラスに使用するステレオタイプです。 entity ( 以下、エンティティ ) はシステムに存在するデータのクラスに使用するステレオタイプです。 control ( 以下、コントロール ) はバウンダリクラスとエンティティクラスの間でデータの加工をするクラスに使用するステレオタイプです。 「カレンダー画面を表示する」ユースケースに登場するクラスをステレオタイプを用いて表現すると図 8 のクラス図のようになります。
 図 8 では DateCellRenderer と DateCell を「カレンダー画面」クラスに統一しました。 また、 CalendarDate と Scheduler の振舞いから、カレンダーに表示する情報を作る役割として「カレンダー情報取得コントロール」を抽出しました。 「カレンダー情報取得コントロール」は日付に対応した「予定一覧」を持っています。 「予定一覧」は ScheduleList の変名です。


図8:「カレンダー画面を表示する」機能のクラス図
図 8 : 「カレンダー画面を表示する」機能のクラス図

 そもそも DateCellRenderer と DateCell が別々のクラスとして定義されたのは Swing アーキテクチャを使用するという、実装判断によります。 今回の分析では、実装に依存した内容は省略するため、「カレンダー画面」という画面表示に限定したクラスを抽出しました。
 CalendarDate と Scheduler は「カレンダー画面を表示する」ユースケースの中で、特定の日付の予定が存在するか教える役割を担っています。 そのため、 2 つの実装クラスから上記の役割を「カレンダー情報取得コントロール」という 1 つのクラスとして抽出しました。
 それでは、あともう一つ既存のユースケースを実現するクラスについて分析してみましょう。 「予定を入力する」ユースケースは図 9 のシーケンス図に登場するクラスで実現されています。 先ほどのクラス図の要領でこのユースケースのクラスをステレオタイプを使ったクラス図に再構成すると、図 10 のようになります。 RegisterDialog を「予定登録画面」に変え、 CalendarDate と Scheduler から「予定登録コントロール」を抽出しました。


図9:予定を登録する機能のシーケンス図 図10:予定を登録する機能のクラス図
図 9 : 予定を登録する機能のシーケンス図 図 10 : 予定を登録する機能のクラス図

 それでは、「カレンダー画面を表示する」クラス図 ( 図 8 )と「予約を入力する」クラス図 ( 図 10 ) の二つのクラス図を見比べてみましょう。 この二つのクラス図の間にはコントロールクラスに差異があります。 この「カレンダー情報取得コントロール」と「予定登録コントロール」に着目してみましょう。 「カレンダー情報取得コントロール」はカレンダー表示に必要な情報を取得するための役割を持つクラス、「予定登録コントロール」は特定の日付の予定を登録するクラスとしっかり役割分担がされているように思えます。 しかし、両方のコントロールクラスが別々に予定一覧への関連をもっているところが、気になります。 実際のプログラムでも「カレンダー情報を表示する」ユースケースの実装も、「予約を入力する」ユースケースの実装も、 Scheduler の getFixedSchedules メソッドを使用して ScheduleList を取得しています。
 このように、一見うまく描けているモデルでも、モデリングを進めていく過程でどこか具合が悪いと感じることがあるでしょう。 そんなときは重要な概念が、クラスや関連として抽出されていない証拠です。 今回の場合は、「カレンダー情報取得コントロール」、「予定登録コントロール」の両方が日付単位に予定一覧を管理しています。 そこで、これらのクラスから日付単位に予定一覧を管理する役割のクラスを抽出しました。 図 11 は予定情報を日付単位に管理するクラスとして「スケジューラ」を追加したクラス図です。


図11:新たにエンティティ・クラス「スケジューラ」を追加
図 11 : 新たにエンティティ・クラス「スケジューラ」を追加

 もう一つ具合が悪いと感じるところがあります。 「スケジューラ」には特定の日付に複数の予定が存在しています。 図 11 の「スケジューラ」、「予定一覧」、「予定」の関連がこれを表現しています。 それでは、図 12 のクラス図を見てください。 このクラス図でもスケジューラには特定の日付に複数の予定が存在していることがわかります。 図 11 と図 12 のクラス図のどちらがよいクラス図でしょう。 私は図 12 のほうがよいと思っています。


図12 : 「スケジューラ」と「予定」の関連案2
図 12 : 「スケジューラ」と「予定」の関連案2

 そもそも、「予定一覧」は ScheduleList クラスから抽出された実装的なクラスです。 分析のクラス図に実装的なクラスが入ってしまうと、システム設計に弊害をもたらします。
 分析のクラス図は「システムは何をするか」のみを定義しなければなりません。 分析のクラス図で「システムをどうやって作ればよいか」を示してしまうことで、どうやって作るか示したクラスが、何をするべきか示したクラスにすげ変わってしまいます。 そして、設計時には、設計の情報が混じった分析のクラス図のクラス構成を強いられてしまいます。 分析のクラス図にシステムは何をするかのみを示すことで、設計時にはより適切なアーキテクチャを選択できるようになります。 そのような理由から、分析時のクラス図には実装のクラスを含めないことを推奨します。

3.2 アラーム機能の分析

 ここまでは既存システムをもとに分析を行いました。 次に新規機能であるアラーム機能の分析をします。 アラーム機能は当然既存システムがないため、ユースケース記述をもとに分析を始めます。 今回作成したユースケース記述の基本フローは以下のものでした。

基本フロー
			
  1. タイマーが、「予定の時間にアラーム表示する」ユースケースを起動する。    
  2. システムは、スケジューラに現在時刻の予定があることを確認する。    
  3. システムは、現在時刻に登録されている予定をアラーム表示する。    
  4. ユーザーは、アラーム表示を確認する。    
			

 アラーム機能を起動するアクターは「タイマー」でした。バウンダリクラスはアクターと対話するクラスなので、「タイマー」をバウンダリクラスとして抽出します。 「タイマー」は一定時間が経過したことを知らせる役割を持つクラスです。
 アラーム機能は最終的にアラーム画面を表示するため、「アラーム画面」をバウンダリクラスとして抽出します。 「アラーム画面」はアラーム画面の表示の仕方を知っているクラスです。
 アラーム表示しなければならない予定を決定するコントロールクラスとして「アラーム機能」を抽出します。 アラームはタイマーの通知により現在時刻を知ります。 アラームが知っている現在時刻を表現するため、アラーム表示時間という関連端ロールをもつ関連を定義しました。
 既存システムのモデルで「スケジューラ」というクラスを抽出しました。 スケジューラは日付単位に予定を管理していましたが、今回のアラーム機能では時間単位に予定を取得できることが望まれるため、現行のスケジューラではこの要求にこたえることはできません。 そのため、時間単位で予定を管理するための関連を追加しました。
 ここまで抽出したクラスと関連を描いたクラス図が図 13 に、処理の流れを描いたシーケンス図が図 14 になります。


図13:アラーム機能のクラス図 図14:アラーム機能のシーケンス図
図 13 : アラーム機能のクラス図 図 14 : アラーム機能のシーケンス図

 先ほど、「スケジューラ」に時間単位で予定を管理する関連を追加しました。 これまでは日付単位の機能のみであったところに、アラームという時間単位の機能が追加されたために、この関連の追加が発生したのです。
 このように従来のシステムでは全く考慮されていない機能が追加された際にはモデルもシステムも大きく変更しなければなりません。 「オブジェクト指向や UML で開発しているからそんなときでも大丈夫」といいたいところですがオブジェクト指向や UML はそんなに万能ではありません。 そのため、将来の追加が予想できる要求に対応したモデル、システムを ( スケジュールとコストを考慮した上で ) 構築することには価値があります。
 もし、「スケジューラ」を日付単位、時間単位以外の単位でも予定を管理できるようなモデルにするならば、図 15 のクラス図のようになるでしょう。 日付と時間の限定子をやめて、時間単位という日付と時間を包括したクラスを限定子とすればよいのです。 このようにすることで、新たに月単位で予定を管理することになっても、時間単位クラスのサブクラスを定義するだけですみます。


図15:機能追加に備えたクラス図
図 15 : 機能追加に備えたクラス図


4.まとめ


 分析のモデルを書き終えた Chen 君のところへ、開発チーム・リーダーの Jun 先輩が様子を見に来ました。

Jun君の発言1
「今日はどこまで進んだかい。」
Chen君の発言1
「はい、まずユースケース図とユースケース記述を書いてアラーム機能の機能要求をまとめました。」
Jun君の発言2
「ふむふむ。 ユースケース図とユースケース記述の形式は外部から見たシステムの振舞いがわかりやすくていいね。」
Chen君の発言1
次にシステムは何をしなければならないのかを分析し、その結果をクラス図とシーケンス図で定義しました。」
Jun君の発言2
「システムは何をするのかという観点で書かれていると、以降の設計作業がやりやすくていいね。 おや、このクラス図 ( 図 15 ) は何?」
Chen君の発言1
「将来の機能追加にも対応できるように工夫しました!」
Jun君の発言2
「がんばっているね。 でももし、開発に時間がかかるようであればここまでしなくても構わないよ。」
Chen君の発言1
「はい。 ( ちょっと落胆 ) 」

 今回はユースケースによるシステムの機能の分析を行い、システムが機能を実現するために何をしなければならないか定義するために、クラス図を利用して分析しました。 ついに次回はアラーム機能の設計を行い、 UML を設計でどのように使えばよいか紹介します。



《参考文献》


脚注 1 : ユースケースを文章形式で定義したドキュメントをシナリオと呼ぶこともありますが、 これは本稿で解説しているユースケース記述とは意味するところが微妙に異なります。 UML Specification のユースケースインスタンス ( UseCaseInstance ) の説明に次の一文があります。 明確に記述されたユースケースインスタンスはシナリオと呼ばれる。」 つまり、シナリオとはユースケースのインスタンス ( 実現例 ) の記述であり、 その意味で本稿で解説しているユースケース記述の基本フロー、代替フローもシナリオといえます。 ちなみに、基本フロー、代替フローは UP ( Unified Process ) 、RUP ( Rational Unified Process ) の用語です。

※この記事は、『Java World』 2002 年 12 月号に掲載された 「Java で学ぼう ! 初めての UML 」に一部加筆・追加したものです。


連載記事一覧

関連記事一覧


© 2002 - 2003 OGIS-RI Co., Ltd.
Prev. Index Next
Prev. Index Next