【設計】クラス図の基本

クラス図のポイント
  • クラス図はシステムの静的構造(クラスとその関係)を可視化する図
  • クラスは「名前」「属性」「操作」で表す
  • 可視性は + - # ~ の記号で表す
  • 関係は 関連・集約・合成・継承・実現・依存 を線や矢印で表す
目次

クラス図とは

クラス図は UML(Unified Modeling Language) における最も基本的かつ重要な図の1つで、ソフトウェアやシステムの構造を可視化するために使われます。

  • 目的:システム内の「クラス」とそれらの「関係」を表現すること。以下のような役割を持ちます。
    • ソフトウェアの静的な構造を把握
    • 設計や仕様の共有
    • 実装への橋渡し
  • 特徴
    • 主にオブジェクト指向開発で使用する
    • 「何が存在するか(構造)」を示すもので、「どのように動くか(動作)」は別の図(※)が担当
    • 設計初期では概念レベルのモデリング、後期では詳細な実装仕様の設計に使える

※動作を示す図:シーケンス図、アクティビティ図など。

クラス図の表記

クラス図は大きく分けて、「クラス」、「可視性(アクセス修飾子)」、「関係」の三つで表記していく図です。
クラス図における各要素の表記方法について以下にまとめます。

クラスの表記

クラスは長方形で表され、通常は「名前」「属性」「操作」の3区画に分けて記述します。

クラス表記:

各区画の基本的な記法は以下の通りになります。

  • クラス名
    • 先頭大文字の単数形が基本(例:User, Order)
  • 属性(フィールド)
    • 書き方:可視性 名前 : 型 = 初期値
    • 例:- id : int = 0
  • 操作(メソッド)
    • 書き方:可視性 名前(引数 : 型) : 戻り値の型
    • 例:+ login(username : String, password : String) : boolean

可視性(アクセス修飾子)の表記

可視性は、クラスの属性や操作(メソッド)がどこから参照・利用できるかを示すものです。
記号で表現され、主に次の4種類があります。

記号可視性意味
+public(公開)どこからでもアクセスできる
-private(非公開)同一クラス内からのみアクセス可能
#protected(保護)同一クラスおよびサブクラスからアクセス可能
~package(デフォルト/パッケージ)同一パッケージ内からアクセス可能

関係の表記

関係は、クラス同士のつながりを矢印や線で表すもので、以下のような種類があります。

関係名意味使用例
関連(Association)
クラス同士が何らかの関係を持つ
(参照/非参照の関係が不定)
プレイヤー ― 所持アイテム
双方向関連
双方が互いを参照キャラクター ↔ 装備
単方向関連
一方向だけが参照UI → ゲームマネージャ
誘導可能性
一方向だけが参照かつ、逆方向からの参照不可を明示プレイヤー ×→ インベントリ
多重度(Multiplicity)
関係数を表すパーティ 1..* ― 0..1 プレイヤー
集約(Aggregation)
弱い「所有」関係(部分は全体無しでも存在)メンバー ―◇ チーム
コンポジション(Composition)
強い「所有」関係(部分は全体に従属)ステータス ―◆ キャラクター
継承(Generalization)
サブクラスがスーパークラスを継承
(汎化関係とも言います)
敵 ー▷ キャラクター
実装(Interface Realization)
クラスがインターフェースを実装プレイヤー入力制御 —▷ IInputHandler
依存(Dependency)
一時的に利用する(引数・ローカル変数など)敵AI —> ナビゲーション
関連クラス
(Association Class)
関係自体に属性を持たせる
(要素間の関連に属性や操作を追加)
キャラ ―<関連クラス:「装備スロット」ー 装備

    クラス図の作成手順

    いかに簡単なクラス図を作成する際の手順をまとめます。

    仕様書から作成

    0. 前提

    • ユースケース・要件書・画面仕様などを用意する

    1. 名詞を見つ

    • 要件や仕様の文章で名詞(Person, 注文, 商品 等)を見つけ、「クラス候補」としてリストアップする。
    • 同じ意味の別名は統一する(例:顧客 = User)。

    2. クラス候補を性質で振り分ける

    • 「単に値を持つだけで振る舞いを持たない」→ 属性として記述
      (:注文日の orderDate : Date、郵便番号 postalCode : String)
    • 振る舞い(処理)を持つなら」→ クラスとして定義し、操作(メソッド) を列挙
      (Order.calculateTotal(): MoneyUser.login(id, pw): boolean)
    • 「不変で同値性が値で決まる」→<<value object>> としてクラス化(属性だけでよい)
      (例:Money、Address)

    3. クラス間の関係整理

    • 「複数クラスに共通の属性や振る舞いがある」→共通のスーパークラス or インタフェースを作る(継承 or 実現
      (判断基準:is-a が明確なら継承、振る舞いだけ共有させたいならインタフェース。)
    • 「クラス間に、A が B を包含する(ホスト/部品)関係がある」→(集約/合成)
      • 部品は独立して存在可能 → 集約(白ひし形)
      • 部品は全体にライフサイクルが縛られる(全体が消えると部品も消える) → 合成(黒ひし形)
        House ◆— Room(合成)、Team ◇— Player(集約)
    • 「クラス間が、B は A の特殊化(例:Dog is-a Animal)で表現できる」→ 継承(汎化)
      注意点:継承は振る舞い/契約の共有に適するが、多用は避ける(コンポジション優先を検討)。
    • 「メソッドの引数やローカルで一時的に参照するだけ」→ 依存 (フィールドとして保持しないことを意味)
    • 実装上どちらのクラスが相手を参照する必要があるか明確
      →関連線に矢印をつけて navigability を表す。不要な双方向参照は避ける。(誘導可能性の記述

    4. 多重度の記述

    • 関係の「個数の制約」がわかるなら 各端に 1 / 0..1 / * / 1..* を書く。
      実装対応例* → List/Set 等、0..1 → Optional / nullable。
    • A と B が多対多で関係する → 中間エンティティ(結合クラス)を作る。
      (例:学生と科目 → 結合クラス:履修)

    5. クラス図の整理

    • ロール名・役割名の記述
      • 同じクラス同士の関連や、端の意味を明確にしたいとき
        関連端に roleName : Type を付ける(例:orderItems : OrderItem [*])。
    • 集約よりコンポジション(合成)を優先すべきか検討
      • ライフサイクルが強く結びつく(部品は単独で意味がない)なら
        → 記述合成(黒ひし形)。特にドメインモデルでは合成が明示的で有益。
    • ユーティリティ/サービスクラスの扱い
      • 状態を持たず、単に処理を提供する(例:価格計算ロジック)なら
        クラス図に含めるか省略かを検討。含めるなら <<service>><<utility>> を付ける
    • 永続化/DB マッピングを考慮
      • クラスが永続化される(エンティティ)なら
        <<entity>> を付け、ID 属性(例:- id : Long)と永続化に必要なキーを明示する。
    • 抽象クラス / インタフェースの扱い
      • 共通振る舞いを定義し、実装を分離したい場合
        抽象メソッドを斜体 or <<abstract>>、インタフェースは <<interface>> で表す。
        実装は破線の実現(実線三角)。
    • 肥大化への対処
      • クラスが属性7個以上またはメソッドが多く責務が複数派生している
        責務ごとに分割(SRP 遵守)、値オブジェクト化、サブクラス化または委譲(composition)へ。
    • 実装用の注記
      • 実装言語が決まっている(Java, Python 等)
        マッピング注記を図の横に書く(例:1..* -> List<OrderItem>0..1 -> Optional<T>)。

    コードからクラス図を作成

    1. クラス宣言を見つけたら

    • 条件classinterfaceenum を見つけたら
    • 記述:クラス図の「クラス」として追加する。
    • 注記
      • interface<<interface>> を付ける
      • abstract class<<abstract>> とする
      • enum<<enum>> として扱う

    2. フィールド(メンバ変数)を見つけたら

    • 条件:クラス内部のフィールドを見つけたら
    • 記述:クラスの「属性」として追加する。
    • 表記ルール
      • 可視性記号をコードに対応させる
      • public+
      • private-
      • protected#
      • (Java のデフォルト / package-private)→ ~
      • 型を記載する(例:- id : int
      • 初期値がある場合は記述可能(例:= 0

    3. メソッドを見つけたら

    • 条件:クラス内部のメソッド定義を見つけたら
    • 記述:クラスの「操作」として追加する。
    • 表記ルール
      • 可視性(+ - # ~)を付ける
      • 引数の型と戻り値の型を記述
      • 例:+ login(username : String, password : String) : boolean
      • abstract メソッドなら斜体表記 or <<abstract>>
      • static メソッドは下線で表記

    4. 継承/実装を見つけたら

    • 条件extends / implements を見つけたら
    • 記述
      • extends継承(汎化:実線+空白三角矢印)
      • implements実現(破線+空白三角矢印)

    5. フィールドの型が別クラスなら

    • 条件:フィールドの型が他のクラス型になっている場合(例:Order クラス内に User user;
    • 記述:クラス間に 関連(実線) を追加。
    • 追記
      • コレクション型(List<OrderItem> など) → 多重度 *
      • Optional<T> や nullable → 多重度 0..1
      • それ以外 → 多重度 1

    6. メソッドの引数や戻り値が別クラスなら

    • 条件:メソッドのシグネチャに他クラスが登場する場合
    • 記述依存(破線矢印) を追加。
    • void register(Order order)このクラス → Order に依存。

    7. 内部クラス/ネストクラスがあるなら

    • 条件:クラスの内部に static classinner class がある場合
    • 記述:クラス図で 入れ子 にするか、関連として示す。

    8. 合成/集約の判定(コードから推測)

    • 合成(黒ひし形):
      • 条件:フィールドがコンストラクタで必ず生成され、ライフサイクルが一致する場合
    • 集約(白ひし形):
      • 条件:フィールドが外部から渡され、独立して存在可能

    9. 誘導可能性(ナビゲーション)を決める

    • 条件
      • フィールドに相手クラスを持っている → 「保持側 → 相手クラス」に矢印を付ける
      • 相手クラスにその逆がなければ片方向の関連になる

    10. 不要なクラスを省く

    • 条件:テスト用、DTO 専用、ユーティリティなど「ドメインの本質に関係ない」クラスを含んでいる場合
    • 実行:クラス図の対象範囲に含めるかどうかを整理する。
    • 備考:必要なら <<utility>> <<service>> <<test>> などを付ける。

    11. パッケージ/モジュールを考慮する

    • 条件:コードに package / 名前空間がある場合
    • 記述:クラス図をパッケージごとにまとめる。

    12. 調整

    • 条件:全クラス・関係を抽出し終えたら
    • 調整:
      • 多重度を補完(1 / 0..1 / *
      • 抽象クラスやインタフェースに注記を付ける
      • 複雑な関係はサブ図やパッケージ図に分割する

    おすすめの本:

    おすすめの本:


    ここまでお読みいただき、ありがとうございます。
    今回紹介した内容が、皆さんの開発のヒントになれば幸いです。

    記事が役に立ったと感じていただけましたら、OFUSE にてご支援いただけますと今後の運営の励みになります。

    OFUSEで応援を送る

    今後もゲーム制作に関するさまざまな情報や、そこから得られた知見を共有していく予定ですので、引き続き当ブログをよろしくお願いいたします。

    • URLをコピーしました!
    目次