3秒でわかる
クラスと属性・操作、そしてクラス同士の関係を1枚に表す UML の図。実装前に責務の置き場所と依存の向きを合意するために描きます。
もう少し詳しく
どういうものか
システムを構成するクラスを箱で表し、その中に属性と操作を並べ、箱と箱を線でつないで関係を示す UML の静的構造図です。1つの箱は上から順に、クラス名、属性、操作の3段に分かれます。
属性や操作の頭に付く記号は可視性を表します。+ が public、- が private、# が protected です。線の種類は関係の種類を表し、実線の矢印は関連、白い三角の矢印は継承、ひし形は全体と部分の関係を示します。
なぜ必要か
コードを書き始めてから「この処理はどのクラスの仕事か」を議論すると、すでに書いた分を捨てることになります。クラス図は箱と線だけなので、書き直しが安く、責務の置き方をその場で何通りも試せます。
もう1つは共有です。文章で「注文は複数の明細を持ち、明細は商品を参照する」と説明するより、1枚の図のほうが読み違いが起きません。設計レビューや引き継ぎで最初に開かれるのがこの図です。
具体例
テキストで表すと下のような形になります。
+-------------------+ +---------------------+
| Order | | OrderItem |
+-------------------+ +---------------------+
| - id: Long |1 *| - quantity: int |
| - orderedAt: Date |<>--------| - unitPrice: int |
+-------------------+ +---------------------+
| + total(): int | | + subtotal(): int |
+-------------------+ +---------------------+1 と * は多重度で、1件の注文が0個以上の明細を持つことを表します。ひし形は集約で、注文が明細を束ねる側であることを示します。
対応するコードは次のようになります。
class Order {
private Long id;
private List<OrderItem> items;
public int total() {
return items.stream().mapToInt(OrderItem::subtotal).sum();
}
}つまずきやすいところ
すべてのクラスを1枚に描こうとして、線が交差した読めない図になるのがよくある失敗です。図の目的は網羅ではなく合意なので、議論したい範囲だけを切り出して描きます。
多重度の書き漏れも品質を下げます。1対多なのか多対多なのかは、テーブル設計とコードの持ち方を直接左右する情報です。ここが空欄の図はレビューで判断ができません。
コードを書いた後に図を更新しない運用も定番です。実装とずれた図は、無いより有害になります。更新し続けないと決めたなら、設計時点の記録として日付を添え、正本はコードだと明記します。
似た用語との違い
クラス図が表すのは静的な構造です。処理の時間的な流れを表したいときはシーケンス図、利用者から見た機能の範囲を示したいときはユースケース図を使います。