![]() |
[2003 年 3 月号] |
[技術講座]
まず初めに、前回の内容を簡単に振り返っておきましょう。
前回は、プログラムの振る舞い ( 機能 ) がどう実現されているかを把握するために、2 種類の相互作用図 - シーケンス図とコラボレーション図 - を作成しました。 主に時間軸に沿ったメッセージの協調過程を表現する場合はシーケンス図を、 主にオブジェクト間の接続関係を表現する場合はコラボレーション図を、それぞれ用途別に使い分けることにより、 複数オブジェクトの協調動作を把握しやすいことがわかりました。
さて、スケジューラ・ソフトの振る舞い ( 機能 ) を調べていた Chen 君は、 オブジェクトがメッセージを受け取ったときの内部状態に依り対応する振る舞いが変化する場合について気になることがあるようです。
![]() |
「予定の有無に依ってセルの描画と画面の操作を変える制御があるなぁ。ちょっと整理しておこう・・・。」 |
---|
ステートチャート図は、 1 つのオブジェクトに着目して、そのオブジェクトが受け取ったイベントに伴い遷移する状態を表現するものです ( *1 ) 。
Chen 君は、メイン画面のセルに着目して、状態と振る舞いを整理しようとしています。 私達も一緒にソース・コードを参照しながら整理した内容をステートチャート図で表現してみましょう。 ここで着目するセルは、カレンダー表示を描画/セルクリックのイベント受信する抽象的なセルとします。
ステートチャート図で表現する要素は「状態 ( state ) 」と「遷移 ( transition ) 」に大別できます。
スケジューラのメイン画面上にて、予定の入っていない日は白色のセル、 予定の入っている日は黄色のセルになっています ( 画面 1 )。 白色のセルをクリックすると新規の予定を登録する画面 2 が表示されますが、 黄色のセルをクリックすると予定を編集/削除/追加する画面 3 が表示されます。
![]() |
![]() |
画面 1 : スケジューラのメイン画面 | 画面 2 : 新規の予定を登録する画面 |
画面 3 : 予定を追加/編集/削除する画面
これらの制御をソース・コードで確認してみましょう。
public class DateCellRenderer extends DefaultTableCellRenderer
{
…略…
public Component getTableCellRendererComponent(
JTable table, Object value,
boolean isSelected, boolean hasFocus,
int row, int column) {
…略…
DateCell dateCell = (DateCell)table.getModel().getValueAt(row, column);
setBackground(dateCell.getColor()); - ( 1 )
…略…
}
…略…
}
セルの描画は、クラス DateCellRenderer が請け負っています。リスト 1 - ( 1 ) にてセルの背景色を設定しているのがわかります。 ここで設定している値は、クラス DateCell のメソッド getColor の戻り値ですね。
public class DateCell
{
private CalendarDate calendarDate;
public Color getColor() {
// 日付でない場合
if (calendarDate == null) return Color.lightGray; - ( 3 )
// 予定がある場合
if (calendarDate.hasSchedules()) {
return Color.yellow; - ( 1 )
}
// 予定がない場合
else {
return Color.white; - ( 2 )
}
}
…略…
}
リスト 2 - ( 1 ) で『予定がある日』に黄色、 2 - ( 2 ) で『予定がない日』に白色を、そして、『日付でない』ときは常に灰色を指定しています ( リスト 2 - ( 3 ) ) 。 セルをクリックすると、クラス CalendarFrame のメソッド table_mouseClicked が呼び出されます。ここで表示する画面を制御しています。
public class CalendarFrame extends JFrame
{
…略…
// セルがマウスクリックされたときに呼び出される処理
void table_mouseClicked(MouseEvent e) {
JTable table = (JTable)e.getSource();
int row = table.rowAtPoint(e.getPoint());
int column = table.columnAtPoint(e.getPoint());
TableModel model = table.getModel();
DateCell dateCell = (DateCell)model.getValueAt(row, column);
CalendarDate date = dateCell.getCalendarDate();
// 日付でない場合
if (date == null) return; - ( 3 )
// 予定がある場合
if (date.hasSchedules()) {
new DateScheduleDialog(this, date).show(); - ( 1 )
}
// 予定がない場合
else {
new RegisterDialog(this, date).show(); - ( 2 )
}
…略…
}
…略…
}
リスト 3 - ( 1 ) で『予定がある日』に予定を編集/削除/追加する画面 DateScheduleDialog を、
リスト 3 - ( 2 ) で『予定のない日』に新しい予定を登録する画面 RegisterDialog を、それぞれ表示します。そして、『日付でない』ときは何もしません。
これらの制御は、いずれもセルが『日付でない』『日付である』そして『日付である』ときには『予定がある日』『予定がない日』を意識した上で行っています。セルが意識しているものを「状態」として捉えます。
これらの状態は変化することがあります。スケジューラのメイン画面上にて白色のセルをクリックして新しい予定を登録すると黄色のセルに変わります。
この変化を「遷移」として捉えます。
図 1 : 状態と遷移の例
ステートチャート図で表現すると図 1 のようになります。状態は丸み付き四角形で 遷移は矢印で、それぞれの要素を示します。状態は、その中に状態名を記述することができます。 遷移には、次のような書式でラベルを付記することができます。
イベント名 ( コンマ区切りパラメタ並び ) [ ガード条件 ] / 動作式
図 2 : 状態内の表記例
また、状態は、水平線で上下に分割して表記することもできます。上下に分割したとき、上段を「名前区画 ( name compartment ) 」下段を「内部遷移区画 ( internal transitions compartment ) 」と呼びます ( 図 2 ) 。 名前区画には、状態名を記述します ( さきほどの図 1 は内部遷移区画を省略したものです ) 。 内部遷移区間には、次のような書式で動作を記述します。動作ラベルには表 1 に挙げているような種類があります。表に挙げた動作以外にも、動作式のトリガとなるイベントを動作ラベルに示すこともあります。いずれも必要なければ省略することが可能です。
動作ラベル / 動作式
動作ラベル | 実行される動作 |
---|---|
entry | 状態入場時に実行される動作を示す。 |
do | 状態にある間に実行される動作を示す。 |
exit | 状態退場時に実行される動作を示す。 |
include | 別の場所で定義された状態を起動するときを特定するための動作を示す。 |
そして、状態には「初期状態 ( initial state ) 」「最終状態 ( final state ) 」といった特別な状態があります。 通常の状態は、入力状態と出力状態を持ちますが、開始状態は入力状態を、終了状態は出力状態を持ちません。 初期状態は黒丸で、最終状態は中が黒い二重丸で要素を示します ( 図 3 )。
図 3 : 初期状態と最終状態の例
では、 Chen 君が作成したステートチャート図を見てみましょう ( 図 4 )。
図 4 : Chen 君が作成したステートチャート図
状態『予定がない日』の内部遷移区画には、次のように動作を記述していますね。
do / セルの背景を白色で描画
マウスクリック / 画面 RegisterDialog を表示
状態『予定がある日』から状態『予定がない日』への遷移には、次のようにイベントを記述しています。
予定を削除する[最後の予定]
一方、イベント
予定を削除する[最後の予定ではない]
のときは自身に遷移しています。このような遷移を「自己遷移 ( self-transition ) 」と呼びます。
参考までに「コンポジット状態 ( composite state ) 」 ( *2 ) について説明しておきます。 状態『予定がある日』と『予定がない日』は、ともに状態『日付である』でもあります。 このような場合、図 5 のように表現することができます。
図 5 : コンポジット状態の例
![]() |
「よし、セルの制御はわかったぞ。最後にシステム全体の流れを整理しておこう。」 |
---|
アクティビティ図は、何かを行っている状態が遷移していく様に着目して、手順 ( 処理の流れ ) を表現するものです。 Chen 君は、システム全体の流れ ( アプリケーションの起動〜終了まで ) を整理するためにアクティビティ図を作成することにしたようです。
public class Main
{
public static void main(String[] args) {
…略…
Scheduler scheduler = new Scheduler();
…略…
scheduler.load(); - ( 1 )
…略…
CalendarFrame frame = new CalendarFrame(scheduler); - ( 2 )
frame.setVisible(true);
}
}
public class CalendarFrame extends JFrame
{
…略…
private Scheduler scheduler;
private void exit() {
…略…
scheduler.save(); - ( 2 )
…略…
System.exit(0);
…略…
}
protected void processWindowEvent(WindowEvent e) {
super.processWindowEvent(e);
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
exit();
}
}
…略…
}
システムを起動すると、リスト 4 - ( 1 ) にて ( ファイル保存されている ) 予定を読み込みます。 その後、リスト 4 - ( 2 ) にてメイン画面を表示していることがわかります。 メイン画面が表示されている間は、ユーザ操作に伴って、新規で予定を登録する画面や予定を編集/削除/追加する画面が表示されます。 そして、ユーザ操作によりメイン画面の右上×ボタンを押下したときに、 クラス CalendarFrame のメソッド processWindowEvent が呼び出されて、 登録されている予定を永続化 ( ファイル保存 ) してからシステムが終了します ( リスト 5 - ( 2 ) ) 。
では、 Chen 君が作成したアクティビティ図を見てみましょう ( 図 6 ) 。
アクティビティ図は、ステートチャート図を拡張したものです。従って、表記法は似通っていますが、 各要素の意味が異なります。また、アクティビティ図にのみ存在する要素もあります。
何かを行っている状態を「アクション状態 ( action state ) 」( *3 ) として捉えます。 アクション状態は、上下は直線で左右の両端が円弧に囲まれた図形で示します。その中に動作を記述します。記述する動作の書式は定義がなく、 自然言語やプログラム言語による擬似コードなどで示すことができます ( 図 6 - ( 1 ) )。 なお、ステートチャート図と同様に、初期状態と最終状態は特別なアクション状態であり、表記法も同じです。 また、入れ子のアクション状態を「サブアクティビティ状態 ( subactivity state ) 」と呼びます。遷移は、矢印で示します ( 図 6 - ( 2 ) ) 。アクション状態が遷移するときの条件を「ガード ( guard ) 」で示すことができます。 ガードは真偽値を持つ条件を [ ] で囲んで遷移に付記します ( 図 6 - ( 3 ) ) 。 遷移する先が分かれる場合には「決断 ( decision ) 」 ( *4 ) を利用します ( 図 6 - ( 4 ) ) 。 決断は、ひし形で示します。ガードが真 ( true ) となる場合に遷移します。
アクション状態をグループ分けするときには「レーン ( swinlane ) 」を利用するといいでしょう。 一般にはクラス、パッケージ、アーキテクチャのレイヤ、サブシステム、組織単位などでグループ分けします。 レーンは縦の実線で区切られた区画で示します ( 図 6 - ( 5 ) ) 。
図 7 : 同期バーの例
Chen 君が作成したアクティビティ図には登場しませんが、 遷移を結合したり分割したりする際に同期を取るときは明示的に 「同期バー ( synchronization bar) 」を利用します ( 図 7 ) 。同期バーは横の太い実線で示します。 分割するときはフォーク ( fork) 、結合するときはジョイン ( join) と呼びます。
*3 ) UML1.2 までは「アクティビティ状態 ( activity state ) 」と「アクション状態 ( action state) 」を、
それぞれ別の要素として定義されていましたが、UML1.3 以降、「アクティビティ状態 ( activity state) 」が廃止され、
「アクション状態 ( action state) 」と「サブアクティビティ状態 ( subactivity state) 」が要素として定義されています。
*4 ) 「決断 ( decision ) 」は「分岐 ( branch ) 」と紹介されている場合があります。
駆け足でアクティビティ図を説明しましたが、ご理解いただけたでしょうか。アクティビティ図は、そこに手順があれば何を対象として記述しても構いませんので、上記の表記法を踏まえて大いに活用してください。
アクティビティ図を書き終えた Chen 君のところへ見計らったように Jun 先輩が姿を現しました。
![]() |
「調子は、どう?」 |
---|---|
![]() |
「ようやく既存のプログラム解析を終えて、システム全体を把握できたところです。」 |
![]() |
「そうか、いよいよ機能追加するんだね。じゃあ、引き続きよろしく。」 |
今回は、振る舞いを表すダイアグラムの続編としてユースケース図とアクティビティ図について説明しました。これにて、既存のプログラム解析は終了いたします。
次回からは、アラーム機能を既存のスケジューラに追加するというケース・スタディに沿って、システム開発における UML ダイアグラムを活用する場面を紹介する予定です。
アクティビティ図は、ステートチャート図を拡張したものですが、 アクティビティ図は状態の内部遷移や明示的なイベントに伴う出力遷移を扱いません。 そして、動作(アクティビティ状態) が完了したことを暗黙的なトリガとして遷移します。 そのため、様々な動作の手順を示すことができるのです。 また、アクティビティ図は、平行処理も扱えます。 そのため、業務フローの整理や、処理が複雑なアルゴリズムの整理などにも適していると言えます。
連載記事一覧
© 2002-2003 OGIS-RI Co., Ltd. |
|