ObjectSquare [2003 年 2 月号]

[技術講座]


Java ではじめる UML

[第2回] クラス図、パッケージ図

伊藤喜一/林俊樹/森三貴/山内亨和/高木栄児/山口健
  前回から始まった本連載、今回からいよいよ Java のソースコードと対比しながら、 UML の各ダイアグラムについて解説していきたいと思います。今回は、 UML の中でも最も使用される「クラス図」と「パッケージ図」についてとりあげます。「クラス図」と「パッケージ図」はシステムの論理的な ( 静的な ) 構造を表すダイアグラムです。

  本記事で使用しているソースコード(スケジューラ)の完全版がダウンロードできます。ご活用ください。


1. ケース・スタディのおさらい

 まず最初に、前回提示したケース・スタディを簡単におさらいしておきたいと思います。

 ある中堅ソフトウェア会社に勤める Chen 君は、リーダーの Jun 先輩から、チームで使用しているスケジューラのドキュメント作成と機能追加を依頼されました。これは、転勤してしまった Mika さんが、以前開発したものです。画面 1 がそのスケジューラのメイン画面です。

 Chen 君が調査してわかった、現在ある機能は、  そして、 Jun 先輩から依頼された追加機能は、 でした。

 スケジューラのメイン画面
画面 1 : スケジューラのメイン画面


2. ソースコードの構成

Chen 君の発言
「スケジューラの機能はだいたいわかったから、今度はソースコードを調べてみよう。えーと、ファイルの構成は・・・ ( リスト 1 )。なるほど、ちゃんとパッケージに整理はされているけど、思ったよりクラス数が多いな。まずは、クラス図をかいて全体の構成をつかむ必要がありそうだ。 domain パッケージあたりから手をつけるといいかな ? 」
 リスト 1 : ソースファイルの構成
- scheduler
  - Main.java
  - dao
    - SchedulerDAOImpl.java
  - domain
    - CalendarDate.java
    - CalendarMonth.java
    - Content.java
    - FixedSchedule.java
    - RepeatedScheduler.java
    - Schedule.java
    - ScheduleList.java
    - Scheduler.java
    - SchedulerDAO.java
    - WeeklySchedule.java
  - gui
    - CalendarFrame.java
    - DateCell.java
    - DateCellRenderer.java
    - DateScheduleDialog.java
    - MonthTable.java
    - MonthTableModel.java
    - RegisterDialog.java
  - util
    - AppUtils.java
    - DateTimeFormat.java
    - DateTimeUtils.java

3. はじめの一歩

 ということで、 domain パッケージの各クラスのソースコードを見ながらクラス図の説明をしていきたいと思います。まずは、一番シンプルな Content クラス ( リスト 2 ) で基本的な部分を押さえましょう。

 リスト 2 : Content.java
package scheduler.domain;

/** スケジュールの内容. */
public class Content {
  private String memo;
  public Content(String memo) {
    this.memo = memo;
  }
  public String getMemo() {
    return memo;
  }
  public void setMemo(String value) {
    memo = value;
  }
  public String toString() {
     return memo;
  }
}

3.1 クラスの表記法

 それでは早速始めます。まず、 Java の「クラス」は UML の「クラス ( class ) 」にそのまま対応します。 UML でクラスは図 1 のように、標準で 3 つの区画を持った実線の長方形で表します。クラス名は一番上の区画の中央に太字で記入します。

 
UML に従って表現された Content クラスその1。クラス、属性、操作が表現され、それぞれの名前、型、可視性の情報を持つ。
図 1 : Content クラス

◆ フィールドの表記法

 Java の「フィールド」を UML で表す方法はいくつかありますが、一番基本的なものは「属性 ( attribute ) 」で表す方法です。次の書式で二番目の区画に記入します。

 可視性 属性名 : 型 = 初期値

 名前と型の順番が Java の文法とは逆になっているので注意してください( *2 )。可視性 ( アクセス制御 ) の表し方は表 1 のとおりです。

表 1 : 可視性( *3 )
記号 Java の修飾子 意味
+ public すべてのクラスからアクセス可能。
# protected サブクラスまたは同一パッケージのクラスからのみアクセス可能。
~ ( 指定なし ) 同一パッケージのクラスからのみアクセス可能。( UML 1.4 から)
- private そのクラス自身からのみアクセス可能。

◆ メソッドの表記法

 Java の「メソッド」は UML の「操作 ( operation ) 」に対応します。次の書式で 3 番目の区画に記入します。

 可視性 操作名 ( パラメータ ) : 戻り値

 やはり名前と型の順番が Java の文法とは逆になっているので注意してください(*2 )。可視性の表し方は属性と同じです。

3.2 クラスの表記法 ( 省略形 )

 いかがですか ? それほど難しくありませんよね。なお、クラスの標準的な表記法は図 1 のとおりですが、そのモデルで特に注目しない要素は適宜省略してよいことになっています。図 2 では、属性の可視性と型、操作の可視性とシグニチャ ( パラメータと戻り値 ) を省略しています。図 3 では属性、操作を区画ごと省略してしまいました。表示する要素を選択することで、そのモデルで表したいことを強調することができます。

 
UML に従って表現された Content クラスその2。可視性とシグニチャが省略されて、クラス、属性、操作のそれぞれの名前のみが表現されている UML に従って表現された Content クラスその3。属性と操作を表現する部分が省略され、クラス名のみの情報を持っている
図 2 : 可視性、シグニチャを省略した Content クラス 図 3 : 属性、操作区画を省略した Content クラス

*2 ) 実は、属性・操作の書式はプログラミング言語の構文でも良いことになっています。
*3 ) 各可視性の意味は、他のプログラミング言語では Java とは若干異なる場合があります。


4. 抽象クラスとインタフェース

 クラスの表記法について、何点か補足しておきたいと思います。

4.1 抽象クラスの表記法

public abstract class Schedule {
  ...
}
 まず、このように abstract をつけて宣言される抽象クラスですが、 UML ではクラス名を斜体にして表します ( 図 4 )。また、フォントの変更だけでわかりにくい場合は、クラス名の後に { abstract } というプロパティをつけて表すこともできます ( 図 5 )。

 
UML で表現された抽象クラスその1。クラス名のみが斜体で表示されている UML で表現された抽象クラスその2。クラス名のみが斜体で表示され、抽象クラスであることを示す{abstract}というプロパティが表示されている。
図 4 : 抽象クラス 図 5 : プロパティを使用した抽象クラス

4.2 インタフェースの表記法

public interface SchedulerDAO {
  public void init();
  public void load(Scheduler scheduler);
  public void save(Scheduler scheduler);
}

 もう一つ、このようにメソッドの宣言だけされ、実装を持たない、インタフェース ( interface ) ですが、 UML ではクラスと同じ長方形を用い、インタフェース名の上にキーワード << interface >> をつけて表します ( 図 6 )。操作を特に明示する必要がない場合は、図 7 のようなロリポップ ( *4 ) と呼ばれるアイコンで表すこともできます。

 
 UML で表現されたインターフェース。長方形の中にインターフェース名や操作名が記述されており、インターフェース名の上部に << interface >> というキーワードが付いている。  UML で表現されたアイコン型のインターフェース
図 6 : インタフェース 図 7 : アイコンで表示されたインタフェース

*4 ) ロリポップ( lollipop ) … ぺろぺろキャンディー


5. 継承と実装

  抽象クラスとインタフェースの説明をしたので、継承 ( 拡張 : extends ) と実装 ( implements ) の表記法も紹介しておきましょう。

5.1 継承の表記法

public class FixedSchedule extends Schedule {
  ...
}
 Java の継承は UML の汎化 ( generalization ) に対応します。子クラスから親クラスへ実線を引き、先端に△をつけて表します( 図 8 )。

 
汎化の図。FixedSchedule クラスから Schedule クラスに対して実線の矢印が引かれており、FixedSchedule クラスが Schedule クラスを実装していることを示している。
図 8 : 汎化

5.2 実装の表記法

public class SchedulerDAOImpl implements SchedulerDAO {
  ...
}
  Java の実装は UML の実現 ( realize ) に対応します。実装クラスからインタフェースに破線を引き、先端に△をつけて表します ( 図 9 )。インタフェースをアイコン表示した場合は、実装クラスとインタフェースを実線で結びます ( 図 10 )。

 
継承の図。SchedulerDAOImpl クラスから SchedulerDAO インターフェースに対して破線の矢印が引かれており、SchedulerDAOImpl クラスが SchedulerDAO インターフェースを継承していることを示している。 継承の図。アイコンで表示された SchedulerDAO インターフェースと SchedulerDAOImpl クラスがくっついており、SchedulerDAOImpl クラスが SchedulerDAO インターフェースを継承していることを示している。
図 9 : インタフェースの実装 図 10 : インタフェースの実装(アイコン表示)

6. フィールド再び ( 関連 )

 先程 Content クラスでは、 Java の「フィールド」を UML の「属性」で表しましたが、もう一つ「関連 ( association ) 」で表す方法があります。

public abstract class Schedule {
  private Content content;
  ...
}

 Schedule クラスは Content クラスへの参照を持っていますが、これを図 11 のように表します。フィールドを持つクラスから型に対応するクラスへ実線矢印を引き、型に対応するクラスの側にフィールド名 ( UML ではロール名 ) を記入します。

 
参照の図。Schedule クラスから Content クラスに実線の矢印が引かれており,Schedule クラスが Content 型のフィールドを持っていることを表している
図 11 : フィールドを関連で表す

 Java のフィールドは、属性と関連のどちらで表すことも可能ですが、主要なクラス間の関係は「属性」より「関連」で記述した方が、システムの概要を把握しやすくなります。およそ表 2 のような基準で使い分けると良いでしょう。値オブジェクトとは、 String や Date など、いくつでもコピー可能で、 equals( ) によって同値性を調べることができるオブジェクト、参照オブジェクトはそれ以外のオブジェクトです。

表 2 : 属性と関連の使い分け
フィールドの型 表現方法 (属性もしくは関連)
基本型 ( boolean、 int、 double など) 属性
値オブジェクト ( String、 Date、 Time、 BigInteger、 BigDecimal など) 属性
参照オブジェクト 関連
配列、コレクション ( List、Map など) 要素による ( 後述 )

7. コレクションと配列

 フィールドの型にコレクションクラス ( List、Map など ) や配列が使われている場合について説明しておきましょう。現在の Java の仕様 ( 最新は 1.4 ) では、コレクションクラスの要素は Object 型になっていますが、 UML で表記する場合は、実際に格納されるオブジェクトの型を意識する必要があります。

7.1 コレクション ( List など )

 ScheduleList クラス ( リスト 3 ) は java.util.List 型のフィールド schedules を持っています。フィールド名からもコレクションの要素が何かおよそ検討はつきますが、add( ) 、remove( ) の引数を見ると、この List に格納されるオブジェクトは Schedule クラス ( またはその子クラス ) のインスタンスであることがわかります ( *5 )。このような場合、 UML では ScheduleList クラスから Schedule クラスへ関連を引き、 Schedule を複数個持てることを示すために、 Schedule の側に多重度 * をつけます ( 図 12 ) 。「多重度」の書き方は表 3 のとおりです ( * と 0..* は同じ意味を表します)。

リスト 3 : ScheduleList.java ( 抜粋 )
package scheduler.domain;
import java.util.*;

/** 予定リスト. */
public class ScheduleList {
  // List<Schedule>
  private List schedules = new ArrayList();
  public List getAll() {
    return schedules;
  }
  public void add(Schedule schedule) {
    schedules.add(schedule);
  }
  public void remove(Schedule schedule) {
     schedules.remove(schedule);
  }
  ...
}
 
ScheduleList クラスから Schedule クラスへと関連が引かれている。
図 12 : コレクションの表し方
表 3 : 多重度の例
多重度の表現例 意味
0..1 0 または 1
1 1
* 0 以上 (上限なし)
0..* 0 以上 (上限なし)
1..* 1 以上 (上限なし)
m..n m 以上 n 以下
m, n m または n

7.2 コレクション ( Map など )

 次に Map ですが、 Scheduler クラス ( リスト 4 ) は java.util.SortedMap 型のフィールド fixedSchedules を持っています。この fixedSchedules に関しては、getFixedSchedules( ) をよく見ると、Date 型の変数 date をキーに ScheduleList オブジェクトを取得していることがわかります ( *6 )。UML ではこれを図 13 のように「限定子つき関連」で表します。限定子が date : Date、関連先のクラスが ScheduleList になります。多重度は限定子 date の「ある値」に対して、 ScheduleList オブジェクトが何個あるかで決定されるので、注意してください。

 
Schedulerクラスの変数 date をキーに ScheduleList オブジェクトを取得するという限定子つき関連の図。
図 13 : 限定子つき関連

7.3 配列

 最後は配列です。同じクラス Scheduler のもう一つのフィールド weeklySchedules を見てみましょう。ScheduleList の配列と宣言され、サイズ DAYS_OF_WEEK = 7 で初期化されています。このように配列の場合は、格納されるオブジェクトの型が明記されていますから、単純にその型へ関連を引き、適当な多重度 ( ここでは 7 ) をつければ完成です( 図 14 )。

 
サイズが 7 である ScheduleList クラスの配列をフィールドに持つ、Schedulerクラスの図。
図 14 : 固定長配列の表し方

 なお、ここまでコレクションや配列の要素が参照オブジェクトの場合を見てきましたが、要素が基本型や値オブジェクトの場合は、普通に属性で表せば良いでしょう。

リスト 4 : Scheduler.java ( 抜粋 )
package scheduler.domain;
import java.sql.Date;
import java.util.*;

/** スケジューラ. */
public class Scheduler {
  static final int DAYS_OF_WEEK = 7;
  // SortedMap<Date, ScheduleList>
  private SortedMap fixedSchedules = new TreeMap();
  private ScheduleList[] weeklySchedules
      = new ScheduleList[DAYS_OF_WEEK];
  public ScheduleList getFixedSchedules(Date date) {
    ScheduleList list = (ScheduleList)fixedSchedules.get(date);
    if (list == null) {
      list = new ScheduleList();
      fixedSchedules.put(date.clone(), list);
    }
    return list;
  }
  public void addFixedSchedule(FixedSchedule schedule) {
    Date date = schedule.getDate();
    getFixedSchedules(date).add(schedule);
  }
  public void removeFixedSchedule(FixedSchedule schedule) {
    Date date = schedule.getDate();
    getFixedSchedules(date).remove(schedule);
  }
  ...
}

*5 ) J2SE 1.5 で導入予定の Java Generics ( JSR - 14 ) では、 List<Schedule> のようにコレクションに格納するクラスを明示できるようになります。
*6 ) 同じく、Java Generics では、SortedMap<Date, ScheduleList> と書くことができます。


8. 依存関係

 クラス図のモデル要素も主要なものはこれで最後です。参照をフィールドに持たないため関連はないのですが、メソッドの引数や戻り値、ローカル変数として使われているクラスとの関係を明示したい場合、「依存関係 ( dependency ) 」を引くことができます。たとえば、 Scheduler クラス ( リスト4 ) であれば、 FixedSchedule クラスが引数で使われているので、依存関係を引くことができます。 UML で依存関係は破線矢印で表します ( 図 15 )。

 
Scheduler クラスから FixedSchedule クラスに対して、依存関係を表す破線の矢印が引かれている。
図 15 : 依存関係

9. domain パッケージのクラス図

 ここまで説明した表記法を用いて domain パッケージのクラスを UML のクラス図で表したのが図 16 です。なお、コンストラクタ、アクセッサ ( get / set )、コレクションの add / remove などは図から省略しています。

 
domain パッケージ内の全クラスの関係を表したクラス図
図 16 : domain パッケージ
Chen君の発言
「よし、これで domain パッケージの構造はだいたいわかったぞ。この調子でほかのパッケージのクラス図も描いてみよう... ( 紙面の都合で省略します ) 」

10. パッケージ

 今回の最後に、プログラム全体の概要を把握するのに便利なパッケージ図を紹介したいと思います。UML でパッケージ ( package ) は図 17 のようなタブつきの長方形で表します。各パッケージに属するクラスの間に、関連 ( 図 18 ) や汎化関係、依存関係がある場合、パッケージ間にも依存関係を引くことができます ( 図 19 )。

 
UML で表現されたパッケージの図
図 17 : パッケージ
 
gui パッケージ内の CalenderFrame クラスから domain パッケージの Scheduler クラスへ関連を表す実線の矢印が引かれた図 gui パッケージから domain パッケージに対して、依存関係を表す破線の矢印が引かれている。
図 18 : 異なるパッケージに属するクラス間の関連 図 19 : パッケージ間の依存関係

 スケジューラの各パッケージ間の関係は調査の結果、図 20 のようになっていることがわかりました。

 
scheduler パッケージ内の各パッケージ (gui、domain、dao、util) の関係
図 20 : スケジューラのパッケージ図

11. まとめ

 今日の作業を終えて、 Chen 君が一息ついていると、 Jun 先輩が様子を見にやってきました。

Jun君の発言1
「どうだい、進み具合は ? 」
Chen君の発言1
「はい、クラス図とパッケージ図はかけました。システムの静的な構造はだいたい理解できたと思います。明日はシステムの振る舞いを調べてみようと思います。」
Jun君の発言2
「どれどれ。うん、なるほど。私はソースコードを見ている時間はちょっとないが、これならひと目見て概要がつかめそうだ。じゃあ、明日も頼むよ。」

 今回、ソースコードと対比しながら、システムの論理的な構造を表す UML のダイアグラム「クラス図」と「パッケージ図」を説明してきましたが、いかがだったでしょうか? 図で表すことで、システムの概要が理解しやすくなったと思いませんか?

 次回は、システムの振る舞いを表す UML のダイアグラムを紹介したいと思います。どうぞご期待ください。



《参考文献》

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


連載記事一覧

関連記事一覧


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