---
title: UML多重度編
tags:  #設計  
author: [Yukiko](https://www.docswell.com/user/yukiko_it)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/VEPKDZ3L78.jpg?width=480
description: UML多重度編 by Yukiko
published: October 01, 26
canonical: https://www.docswell.com/s/yukiko_it/Z8ND97-2026-10-01-220313
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/VEPKDZ3L78.jpg)

シ リ ーズ 第 3部 ： 数字 が 語 る 設 計の 制 約
UML多重度編
「1」と「0..*」を読み解く
1
0..*
線の両端の小さな数字が、コードとデータを決めている
査読済み実証研究 × OMG公式仕様 × Python公式ドキュメント に基づく構成
うさうさ研修工房 ｜ 監修：うさうさ先生


# Page. 2

![Page Image](https://bcdn.docswell.com/page/27VV1W467Q.jpg)

ROADMAP
この資料の歩き方
C H AP T E R 1
C H AP T E R 2
C H AP T E R 3
多重度の読み方
Pythonでの表現
多対多と関連クラス
記法一覧／どちら側の数字か
1 ／ 0..1 ／ 0..* ／ 1..* ／ n..m
双方向参照／中間クラス
C H AP T E R 4
C H AP T E R 5
C H AP T E R 6
落とし穴と設計
総合演習
根拠と早見表
可変デフォルト引数／集合の選び方
受講申込システムを設計する
査読研究／公式リンク／チートシート
多重度は「線の飾り」ではありません。ここを読み違えると、実装もデータベースも丸ごと作り直しになります。
うさうさ研修工房｜UML多重度編
2


# Page. 3

![Page Image](https://bcdn.docswell.com/page/5JGL2D127L.jpg)

C HAPT ER 1
多重度の読み方
線の両端にある数字は、何を数えているのか


# Page. 4

![Page Image](https://bcdn.docswell.com/page/47QYP1D9EP.jpg)

WHAT IS M ULTIPLIC ITY
多重度 —「何個と関係できるか」を示す数字
Customer
1
0..*
+ name : str
Order
+ order_id : str
この2つの数字が「多重度（multiplicity）」。関連の両端に、それぞれ独立に付きます。
最小値 .. 最大値
0..* なら「最小0個・最大は無制限」の意味
数字ひとつなら最小＝最大
1 は「ちょうど1個」、つまり 1..1 の省略形
* は「上限なし」
0..* は * と省略して書かれることもある
うさうさ研修工房｜UML多重度編
4


# Page. 5

![Page Image](https://bcdn.docswell.com/page/KE4WYRGRJ1.jpg)

NOT ATION T AB LE
多重度の記法一覧
記法
意味
読み方の例
Pythonでの表現
1
ちょうど1個（必須）
注文には必ず1人の顧客がいる
self.x = x（必須引数）
0..1
0個または1個（任意）
会員に配偶者情報があれば持つ
self.x: X | None = None
0..*
0個以上（制限なし）
顧客は注文を1件も持たなくてよい
self.xs: list[X] = []
*
0..* の省略形
上と同じ意味
self.xs: list[X] = []
1..*
1個以上（最低1個は必須）
注文には必ず1つ以上の明細がある
list ＋ 空チェック
2..5
2個以上5個以下
チームは2〜5名で構成する
list ＋ 範囲チェック
※ 多重度は OMG UML 2.5.1 仕様の MultiplicityElement として定義されています（詳細は公式仕様を参照）。
うさうさ研修工房｜UML多重度編
5


# Page. 6

![Page Image](https://bcdn.docswell.com/page/L71Y6QDVJG.jpg)

WHICH END COUNTS WHAT
最大のつまずき —「どちら側の数字か」
Customer
1
0..*
Order
○ 正しい読み方
× ありがちな誤読
Order 側の「0..*」は、
『1人の Customer から見て、Order は0件以上』
「Order が0..*個ある」と単独で読んでしまう。
どちらから見た数なのかが抜け落ちる。
覚え方：数字は「遠い側のクラスから見た個数」。自分の隣にある数字ではなく、線の向こうにいる相手から自分を数える。
この1枚の読み違えが、そのまま「リストにすべき所を単一の属性にしてしまう」実装ミスに直結します。
うさうさ研修工房｜UML多重度編
6


# Page. 7

![Page Image](https://bcdn.docswell.com/page/G7WGWMY3E2.jpg)

C HAPT ER 2
Pythonでの表現
数字ごとに、書くべきコードは決まっている


# Page. 8

![Page Image](https://bcdn.docswell.com/page/4JZL5QXGE3.jpg)

EXACT LY ON E
「1」— 必ず1個。省略を許さない
Order
0..*
1
Customer
設計上の意味
「1」は“無くてもよい状態を作らせない”という宣言です。顧客のいな
い注文は、そもそもインスタンスとして存在できない形にします。
必須にする書き方
class Order:
def __init__(self, customer: Customer):
# 引数にデフォルト値を付けない
# ＝ 省略できない＝必須
self.customer = customer
Order(customer=c)
Order()
# OK
# TypeError
# 必須漏れが実行時に即わかる
ポイント：デフォルト値 None を付けた瞬間、それは多重度「1」ではなく「0..1」の実装になります。
うさうさ研修工房｜UML多重度編
8


# Page. 9

![Page Image](https://bcdn.docswell.com/page/YE6W964QEV.jpg)

OPTION AL: ZERO OR ONE
「0..1」— あるかもしれないし、ないかもしれない
Member
1
0..1
Coupon
使う側の責任が増える
0..1 にすると、参照するたびに None チェックが必要になります。「無い
」という状態を全員が扱わねばならない、というコストを受け入れる判断
です。
任意にする書き方
from typing import Optional
class Member:
def __init__(self, coupon: Optional[Coupon] = None):
self.coupon = coupon
# None を許す
# 毎回これが必要になる
if member.coupon is not None:
price -= member.coupon.amount
Python 3.10以降は Optional[Coupon] を Coupon | None とも書けます（公式：typing）。
うさうさ研修工房｜UML多重度編
9


# Page. 10

![Page Image](https://bcdn.docswell.com/page/GE5MN6Q2E4.jpg)

ZERO O R MANY
「0..*」— 0個以上。空のリストから始める
Customer
1
0..*
Order
空がふつうの状態
0..* では「1件もない」が正常です。登録直後の顧客は注文0件。空リスト
をエラー扱いしないこと、そして None ではなく [] で初期化することが
要点です。
0..* の書き方
class Customer:
def __init__(self, name: str):
self.name = name
self.orders: list[Order] = []
def add_order(self, order: Order):
self.orders.append(order)
うさうさ研修工房｜UML多重度編
c = Customer(&quot;うさうさ&quot;)
len(c.orders)
# 0 は正常
for o in c.orders:
...
# 0件なら
# 単に回らない
10


# Page. 11

![Page Image](https://bcdn.docswell.com/page/97295MP1JR.jpg)

ONE OR MANY
「1..*」— 1個以上。空を作らせない仕掛けが要る
Order
1
1..*
OrderItem
ここが落とし穴
0..* との違いは「最小値1」だけ。しかしコード上は、生成時の
チェックに加えて“削除時のチェック”も必要になります。片方
だけだと多重度が守られません。
1..* の書き方
class Order:
def __init__(self, items: list[OrderItem]):
if not items:
raise ValueError(&quot;明細は1件以上必要です&quot;)
self.items = items
def remove_item(self, item):
if len(self.items) == 1:
raise ValueError(&quot;最後の1件は削除できません&quot;)
self.items.remove(item)
うさうさ研修工房｜UML多重度編
型では表せない
list[OrderItem] という型は「空でないリスト」を表現できませ
ん。多重度の下限は、型ではなく実行時の検証で守ります。
11


# Page. 12

![Page Image](https://bcdn.docswell.com/page/DJY4KX547M.jpg)

BOUN DED RANGE
「2..5」— 上限と下限の両方を持つ
Team
1
2..5
Player
数字は定数にする
2 や 5 をコードに直接書くと、仕様変更のたびに散らばった箇
所を探すことになります。図に書かれた多重度は、そのまま名
前付き定数にしておきます。
範囲チェックの書き方
MIN_PLAYERS, MAX_PLAYERS = 2, 5
class Team:
def __init__(self, players: list[Player]):
self._validate(players)
self.players = players
@staticmethod
def _validate(players):
if not MIN_PLAYERS &lt;= len(players) &lt;= MAX_PLAYERS:
raise ValueError(&quot;メンバーは2〜5名です&quot;)
うさうさ研修工房｜UML多重度編
検証は1か所に集める
生成時・追加時・削除時のすべてから同じ検証メソッドを呼ぶ
形にすると、多重度の定義が1か所に留まります。
12


# Page. 13

![Page Image](https://bcdn.docswell.com/page/V7NYR1N6E8.jpg)

C HAPT ER 3
多対多と関連クラス
「両側が * 」のときに何が起きるか


# Page. 14

![Page Image](https://bcdn.docswell.com/page/YJ9PM5RR73.jpg)

ONE-TO-MANY, B OTH D IR ECTIONS
1対多 — 両方向から辿れるようにするか
Customer
1
+ orders
双方向の同期
Order
0..*
+ customer
双方向にする代償
class Customer:
def add_order(self, order: &quot;Order&quot;):
self.orders.append(order)
order.customer = self
# 相手側も更新する
「顧客→注文」と「注文→顧客」の両方を持つと、追加・削除
のたびに2か所を更新する義務が生まれます。片方を忘れた瞬間
にデータが矛盾します。
# 片側だけ更新すると、両者の言い分が食い違う
本当に両方向から辿る必要があるか、先に確かめてください。
実務では、更新用のメソッドを1つだけ公開し、そこから両側を必ず書き換える形にするのが定石です。
うさうさ研修工房｜UML多重度編
14


# Page. 15

![Page Image](https://bcdn.docswell.com/page/GJ8DLYWGJD.jpg)

MANY-TO-MANY &amp; AS SOCIATION CLASS
多対多 — 関係そのものが情報を持ち始める
そのまま多対多にすると
Student
置き場所がない
0..*
「いつ申し込んだか」「成績は何点か」は、Student のものでも Course
のものでもありません。両側が * のとき、関係そのものが属性を持ち始
めます。
Course
0..*
中間クラスに分解する
Student
1
Enrollment
0..*
0..*
1
Course
+ date
+ grade
多対多が「1対多」2本に分かれ、日付や成績の置き場所ができる
enrollment.py
class Enrollment:
# 関連そのものをクラスにする
def __init__(self, student: Student, course: Course, date: str):
self.student = student
# 1
self.course = course
# 1
self.date = date
# 関係が持つ情報
うさうさ研修工房｜UML多重度編
15


# Page. 16

![Page Image](https://bcdn.docswell.com/page/LJLML4NXER.jpg)

C HAPT ER 4
落とし穴と設計判断
Python特有の罠と、集合の選び方


# Page. 17

![Page Image](https://bcdn.docswell.com/page/47MYMKX37W.jpg)

TH E MUT ABLE DEFAULT T RAP
0..* で最も多い事故 — 可変デフォルト引数
× やってはいけない
class Customer:
def __init__(self, orders=[]):
self.orders = orders
○ 正しい書き方
class Customer:
def __init__(self, orders=None):
self.orders = orders or []
何が起きるのか
デフォルト値の [] は関数定義のときに一度だけ作られ、以後すべてのインスタンスで同じリストが共有されます。つまり、A社の注文を追加したつもりが、B社
の注文一覧にも現れます。多重度 0..* を実装したつもりが、全インスタンス共有の1本のリストになってしまう事故です。
a, b = Customer(), Customer()
a.orders.append(&quot;注文1&quot;)
print(b.orders)
# [&#039;注文1&#039;] ← 別の顧客なのに混ざる
うさうさ研修工房｜UML多重度編
17


# Page. 18

![Page Image](https://bcdn.docswell.com/page/P7R9VXNRE9.jpg)

CH OOSING THE C OLLECTION
「多」をどの型で持つか — list / set / dict
型
重複
順序
向いている多重度
例
list
許す
保つ
順番に意味がある 0..*
注文明細の並び
set
許さない
保たない
重複してはいけない 0..*
タグ、権限
dict
キーは一意
挿入順を保つ
キーで引きたい 0..*
商品ID → 在庫数
UMLで区別したいとき
判断の順序
順序を保つことを明示したい場合は関連端に {ordered}、重複を許さない場合は
{unique} と書きます。無指定は「順不同・重複なし」が既定です。
まず「重複してよいか」、次に「順番に意味があるか」。この2つが決まれば型は
自動的に決まります。迷ったら list から始めて構いません。
うさうさ研修工房｜UML多重度編
18


# Page. 19

![Page Image](https://bcdn.docswell.com/page/PJXQZ6NY7X.jpg)

WHEN MULTIPLICITY CH ANGES
多重度が変わると、何が壊れるか
仕様変更：「1人の会員が持てる住所は1件」→「複数登録できるようにしたい」（1 → 1..*）
クラスの属性
self.address → self.addresses（単数形が複数形になる）
参照している全箇所
member.address.city のような記述がすべて書き換え対象になる
データベース
列として持っていたものを、別テーブルに切り出す必要が出る
画面・帳票
1件前提のレイアウトが破綻する。「どれを代表として出すか」の仕様が新たに要る
だから多重度は、図を描く段階で関係者に確認しておく価値があります。「今は1件だが将来増えるか」を最初に聞くだけで、後の手戻りが大きく減ります。
うさうさ研修工房｜UML多重度編
19


# Page. 20

![Page Image](https://bcdn.docswell.com/page/3JK981N4JD.jpg)

C HAPT ER 5
総合演習
受講申込システムの多重度を決める


# Page. 21

![Page Image](https://bcdn.docswell.com/page/LE3W2QVDE5.jpg)

CAS E STU DY: DIAGRAM
総合演習 — 研修の受講申込システム
Student
1
0..*
+ name
Enrollment
0..*
+ applied_at
Course
1
+ title
1..2
Trainer
+ name
決めた多重度とその理由
Student → Enrollment
0..*
申込0件の受講者もいる（登録だけ済んだ状態）
Enrollment → Course
1
申込は必ず1つの講座に紐づく
Course → Trainer
1..2
講師は1名、または2名の共同開催まで
うさうさ研修工房｜UML多重度編
21


# Page. 22

![Page Image](https://bcdn.docswell.com/page/8EDKZ9827G.jpg)

CAS E STU DY: C ODE
多重度をそのまま守るコードにする
enrollment.py
course.py
class Student:
def __init__(self, name: str):
self.name = name
# 0..* → 空リストで開始
self.enrollments: list[&quot;Enrollment&quot;] = []
MIN_TRAINERS, MAX_TRAINERS = 1, 2
class Enrollment:
def __init__(self, student: Student,
course: &quot;Course&quot;, applied_at: str):
# 1 → デフォルト値なし＝必須
self.student = student
self.course = course
self.applied_at = applied_at
student.enrollments.append(self)
うさうさ研修工房｜UML多重度編
class Course:
def __init__(self, title: str,
trainers: list[&quot;Trainer&quot;]):
self._validate(trainers)
self.title = title
# 1..2 → 検証を通ったリスト
self.trainers = trainers
@staticmethod
def _validate(trainers):
n = len(trainers)
if not MIN_TRAINERS &lt;= n &lt;= MAX_TRAINERS:
raise ValueError(&quot;講師は1〜2名です&quot;)
22


# Page. 23

![Page Image](https://bcdn.docswell.com/page/V7PKDZ8LJ8.jpg)

EVIDEN CE-BASE D DES IGN
査読済み研究が示す「多重度の重み」
① 多重度（カーディナリティ）は概念モデルの中核概念
実体間の関係を1対1・1対多・多対多として捉える枠組みが提示され、以後のデータモデリングとUMLの関連表記の土台になった。
出典：Chen, P. P. (1976). The Entity-Relationship Model. ACM Transactions on Database Systems, 1(1), 9–36
② 「0..」を安易に使うと、深い理解を妨げうる
任意（最小0）の属性や関連を含む図は、表面的な把握には向くが、利用者が業務の意味を深く理解する場面では理解を損なうと予測され、3つの実験で支持された。
出典：Bodart, Patel, Sim &amp; Weber (2001). Information Systems Research, 12(4), 384–405
③ 必須と任意の選択は、図の分かりやすさに実際に影響する
必須の属性・関連で構成した図と、任意を含む図を比較する実証研究が行われ、モデルの複雑さと明快さの観点から両者の違いが分析されている。
出典：Gemino, A. &amp; Wand, Y. (2005). Data &amp; Knowledge Engineering, 55(3), 301–326
→ 「0..1 や 0..* を使うなら、なぜ任意なのかを説明できるようにしておく」。この姿勢が、図の読み手を助けます。
うさうさ研修工房｜UML多重度編
23


# Page. 24

![Page Image](https://bcdn.docswell.com/page/2JVV1WN6JQ.jpg)

OFFICIAL SOURC ES &amp; REFE RENC ES
一次情報にあたるための参考リンク
公式仕様・公式ドキュメント
OMG UML 2.5.1 Specification
https://www.omg.org/spec/UML/
typing — 型ヒント（Optional）
https://docs.python.org/ja/3/library/typing.html
組み込み型（list / set / dict）
https://docs.python.org/ja/3/library/stdtypes.html
よくある落とし穴（既定値の共有）
https://docs.python.org/ja/3/faq/programming.html
査読済み参考論文
Chen, P. P. (1976). The Entity-Relationship Model — Toward a Unified View of Data. ACM Transactions on Database Systems, 1(1), 9–36.
Bodart, F., Patel, A., Sim, M. &amp; Weber, R. (2001). Should Optional Properties Be Used in Conceptual Modelling? Information Systems Research, 12(4), 384–405.
Gemino, A. &amp; Wand, Y. (2005). Complexity and Clarity in Conceptual Modeling: Comparison of Mandatory and Optional Properties. Data &amp; Knowledge Engineering, 55(3), 301–326.
※ シリーズ第1部（UMLクラス図編）・第2部（オブジェクト指向Python実装編）と併せてご活用ください。
うさうさ研修工房｜UML多重度編
24


# Page. 25

![Page Image](https://bcdn.docswell.com/page/5EGL2DK2JL.jpg)

CH EAT SH EET
困ったときの1枚チートシート
多重度 → Python
どの型で持つか
最大の罠
1
self.x = x（必須引数）
list
順番に意味がある
0..1
x: X | None = None
set
重複させたくない
0..*
self.xs: list[X] = []
dict
キーで引きたい
1..*
list ＋ 空チェック
2..5
list ＋ 範囲チェック
*
0..* と同じ
def __init__(self, xs=[]):
→ 全インスタンスで共有される
def __init__(self, xs=None):
self.xs = xs or []
下限を守るコード
決める前に聞くこと
if not items:
raise ValueError(...)
if len(self.items) == 1:
raise ValueError(...)
「0件のことはありますか？」→ 下限0か1か
読む向き
「上限はありますか？」→ * か n..m か
数字は「線の向こう側のクラスから見た個数」。
A ─1──0..*─ B なら
『A 1件につき B は0件以上』
UMLの補助記法
「将来増える可能性は？」→ 1 か 1..* か
うさうさ研修工房｜UML多重度編
{ordered}
順序を保つ
{unique}
重複を許さない
「順番に意味はありますか？」→ list か set か
25


# Page. 26

![Page Image](https://bcdn.docswell.com/page/4JQYP1N97P.jpg)

まとめ
• 多重度は「最小値..最大値」。数字ひとつなら最小＝最大
• 数字は“線の向こう側から見た個数”。読む向きを間違えない
• 1 は必須引数、0..1 は None 許容、0..* は空リストで始める
• 1..* や n..m の下限・上限は、型では守れない。検証コードで守る
• 両側が * なら、関連クラスに分解できないか考える
• 0..* の実装で最も多い事故は、可変デフォルト引数の共有
次の一歩
手元の仕様書から関係を1つ選び、両端の多重度を書き込んでみてください。数字が決められない箇所
こそ、業務側にまだ確認が残っている部分です。
うさうさ研修工房
｜
監修：うさうさ先生
1
A
0..*
B


