はじめに
PlantUML は、シンプルなテキスト記述から各種UMLダイアグラムを生成できるオープンソースツールです。テキストベースで記述できるため設計内容をコード同様にバージョン管理しやすく、Gitでの管理とも相性が良い点が魅力です。特にPythonエンジニアにとっては、PlantUMLをPythonスクリプトから呼び出して図を自動生成することも可能で、シーケンス図やクラス図などを手軽に作成できます。本記事では、シーケンス図と状態遷移図(ステートマシン図)に焦点を当て、Pythonの考え方に対応づけながらPlantUMLの基本的な使い方を解説します。物理実験装置や衛星開発、観測機器のオペレーションといった場面で現れるシーケンスや状態遷移に使えるよう分かりやすく説明してみます
初めての方向け : まずはオンラインでやってみよう
初めての方は、下記のコードをコピーして、
@startuml
start
:User Submits Order;
if (Check Warehouse Stock?) then (In Stock)
if (Process Payment) then (Payment Success)
if (Dispatch Order) then (Dispatch Success)
:Show Success;
else (Dispatch Failed)
:Show Error;
endif
else (Payment Failed)
:Show Error;
endif
else (No Stock)
:Show Error;
endif
stop
@enduml
を、
にある、plantuml のオンラインのインターフェースにコピペしてみてください。
次のような図を生成されるはずです。(qiita は plantuml というコード指定で可視化します。)
PlantUML以外の選択肢
PlantUML以外にも似たようなツールがあるので、長所と短所を整理しておきます。
ツール名 | URL | 長所 | 短所 |
---|---|---|---|
PlantUML | https://plantuml.com/ | 多彩な図に対応、カスタマイズ性高い | Java依存、構文がやや複雑 |
Mermaid | https://mermaid-js.github.io | Markdown埋め込み可、軽量、ブラウザ対応 | 表現力に制限あり、詳細制御が難しい |
draw.io | https://www.diagrams.net/ | GUIで直感的、チーム共有に便利 | ソースコード管理に不向き |
Graphviz | https://graphviz.org/ | グラフ構造やDAG(有向非巡回グラフ)可視化に強い | シーケンスや状態遷移図には不向き |
D2 | https://d2lang.com/ | 軽量、次世代的、Mermaidより表現力が高め | 開発中でツールや環境が限定的 |
ここでは、汎用性の高いPlantUMLについて紹介します。
シーケンス図とは?
シーケンス図は、システム内のオブジェクトやコンポーネントなどの参加者(パーティシパント、エンティティ)が時間軸に沿ってどのように相互作用するか、一連の処理の流れをメッセージのやり取りとして表現する図です。オブジェクト間のメソッド呼び出しやデータの受け渡しなど、「誰が」「誰に」「何を」 実行するかを時系列で示すことで、処理手順やオブジェクト間の責務分担を視覚化できます。
開発時には「どのクラスがどの処理を担うか」「複数のモジュールがどの順番でやりとりするか」を整理するのに有用で、既存実装のトレースやユースケースの確認にも利用されます。Pythonの関数呼び出しやオブジェクトメソッドの呼び出しに馴染みのある読者であれば、シーケンス図はプログラム内の関数コールフローを図式化したものと捉えると理解しやすいでしょう。
PlantUMLでシーケンス図を描く
PlantUMLでは、シーケンス図をテキストで記述する際に以下のような基本構文を用います。
-
参加者(Participants)の定義:
シーケンス図に登場する主体をparticipant
キーワードで定義します。人(アクター)を表す場合はactor
キーワードを使うとシルエットが人型になり、それ以外のシステムや外部サービスはparticipant
(デフォルトのボックス表示)で定義します。オブジェクト名に別名をつけたい場合はas
を使ってエイリアスを指定できます。例えば、actor ユーザ as User
のように書くと「ユーザ」という表示名のアクターを定義し、内部ではUser
という別名で参照できます。 -
メッセージ(矢印)の記述:
矢印演算子->
でメッセージ送信(メソッド呼び出しやデータ送信など)を表現します。左辺に送信元、右辺に宛先を書き、コロン:
に続けてメッセージ名や説明を記述します。例えば、A -> B : 実行要求
と書けば「AがBに対して『実行要求』というメッセージを送る」ことを表します。戻りメッセージや応答は実線の矢印の代わりに点線の矢印(-->
)で描くこともできます(この場合、PlantUMLでは同期/非同期の表現として用いられますが、基本的な図では深く気にしなくても構いません)。メッセージ行はコメント('
から行末)を付けて補足説明することも可能です。 -
アクティブ期間(ライフライン)の明示:
activate オブジェクト名
とdeactivate オブジェクト名
を使うと、あるオブジェクトが処理を実行している期間(ライフライン上の細長い長方形)を強調表示できます。例えば、activate Server
~deactivate Server
でサーバーオブジェクトの処理区間を図中にハイライトできます。これは「あるオブジェクトが処理中であること/応答待ちであること」を明示し、処理の開始・終了タイミングを表現するのに役立ちます。ただし、簡単な図では必須ではありませんので、必要に応じて使用すると良いでしょう。
以上の基本構文を踏まえて、実際にPlantUMLでシーケンス図を描いてみます。以下は「ユーザがPCに対して入力を行い、その結果がディスプレイに表示される」というシンプルな対話のシーケンス図の例です。
@startuml
actor ユーザ
participant PC
participant ディスプレイ
ユーザ -> PC : キー入力
PC -> ディスプレイ : 文字を表示
ディスプレイ -> ユーザ : 表示結果
@enduml
上のPlantUMLコードを、下記に貼り付けると、このようなシーケンス図が得られます(qiita
では```plantuml で記述すると画像で表示します)。
図では左に人型アイコンの「ユーザ」、中央に「PC」、右に「ディスプレイ」が配置され、それぞれ実線矢印でメッセージの流れが描画されています。例えば「ユーザ -> PC : キー入力」はユーザからPCへの入力操作を表現し、続く「PC -> ディスプレイ : 文字を表示」でPCがディスプレイに文字出力を行い、その結果を「ディスプレイ -> ユーザ : 表示結果」でユーザが目にする、といった一連の流れを示しています。
このように、シーケンス図を読むことで処理の流れと担当箇所が一目で把握できます。特に、Pythonコードで書かれた関数呼び出しの連鎖やオブジェクト間メッセージのやり取りを視覚化するのに便利です。
Python視点で見るシーケンス図
上記のシーケンス図の内容は、Pythonのオブジェクトや関数で擬似的に表現すると以下のようになります。
class Display:
def output(self, text):
print(f"[ディスプレイ] {text}")
class PC:
def __init__(self, display):
self.display = display
def receive_input(self, text):
print(f"[PC] ユーザ入力を受信: {text}")
# 入力を処理しディスプレイに出力
self.display.output(text)
class User:
def __init__(self, pc):
self.pc = pc
def type_input(self, text):
print(f"[ユーザ] PCに入力: {text}")
self.pc.receive_input(text)
# オブジェクトの準備
display = Display()
pc = PC(display)
user = User(pc)
# シーケンス実行のシミュレーション
user.type_input("Hello")
# 出力例:
# [ユーザ] PCに入力: Hello
# [PC] ユーザ入力を受信: Hello
# [ディスプレイ] Hello
上のコードでは、User.type_input
メソッドがPCに入力を送り(ユーザからPCへの矢印)、PC側でreceive_input
メソッドがその入力を受け取りディスプレイのoutput
メソッドを呼び出しています(PCからディスプレイへの矢印)。ディスプレイのoutput
では単純にテキストを表示するだけですが、これがシーケンス図中のディスプレイからユーザへの点線矢印(「表示結果」)に対応すると考えてください。実際のコードではディスプレイからユーザへ直接通知することはないものの、ユーザがディスプレイを見て結果を知るという受動的な関係を図ではメッセージとして表現しています。
このように、シーケンス図の各矢印はメソッド呼び出しや関数の実行に相当し、コード上の処理フローと対応しています。Pythonでクラス間のメソッド呼び出しを追っていくイメージでシーケンス図を描くと、プログラムの動的な振る舞いを整理しやすくなるでしょう。
状態遷移図とは?
次に状態遷移図(ステートマシン図)を見ていきます。状態遷移図は、システムやオブジェクトが取り得るさまざまな状態と、それらの間で起こり得る遷移(状態の変化)を視覚的に表現した図です。形式的には「有限状態マシン(Finite State Machine, FSM)」の考え方に基づいており、「ある状態で特定のイベントが起こったら、別の状態(または同じ状態)に遷移する」という振る舞いを記述します。各遷移にはしばしばトリガーとなるイベントや条件が付加され、システムが時間経過とイベント発生によってどのように状態を推移していくかを表します。
例えば、物理実験装置を考えてみましょう。装置には「待機中」「計測中」「エラー発生中」「停止」などの状態があり、ユーザが「計測開始ボタン」を押す、計測が完了する、異常が検出される、といったイベントによって状態が変化します。状態遷移図ではこれらを丸や角丸四角の「状態」ノードと**矢印の「遷移」**で表し、矢印には遷移を引き起こすイベント名をラベルとして記載します。こうした図を描くことで、システムのライフサイクル全体や異常系も含めた振る舞いを俯瞰できます。
PlantUMLで状態遷移図を描く
PlantUMLで状態遷移図(ステート図)を記述する基本構文は以下の通りです。
-
状態の定義:
状態はstate
コマンドで定義します。例えば、単純にstate Idle
と書けば「Idle」という名前の状態が定義されます。状態名に空白や記号が含まれる場合や、図中に表示する名前と内部識別子を分けたい場合は、state "表示名" as 別名
の形式で 表示名 と 別名(エイリアス) を設定できます。別名をつけておけば、遷移の記述を簡潔にしたり、名前変更にも柔軟に対応できます。長い名前と別名が同じで良い場合はas
以降を省略可能です(つまり前述の簡易なstate Idle
でOK)。 -
遷移(矢印)の記述:
状態間の遷移は、状態A --> 状態B
のように矢印-->
で二つの状態名(もしくは別名)を結んで表現します。矢印の上には必要に応じてイベント名や条件を:
の後ろに記述できます。例えばLocked --> Unlocked : コイン投入
と書けば、「Locked状態でコイン投入イベントが発生するとUnlocked状態に遷移する」ことを意味します。イベント名を省略すれば単なる状態遷移(常時または内部処理による遷移)を表せます。なお、同じ状態への自己遷移もState --> State : Event
と記述できます。 -
開始状態・終了状態:
UML状態図には、特別な 開始状態(開始ノード)と 終了状態(終了ノード)が存在します。PlantUMLではどちらも[*]
という記号で表現します。開始状態は図のエントリポイントで、通常はそこから最初の状態への矢印を描きます。同様に、終了状態は最終的な停止/完了を示すもので、ある状態から終了状態[*]
へ矢印を描くことで「システムが停止する」などの終端を表現できます(複数描いても構いません)。開始状態から最初の状態への遷移、および各状態から終了状態への遷移を適切に記述することで、状態図が完結します。
以上の構文を踏まえ、具体例としてストップウォッチの状態遷移図をPlantUMLで描いてみましょう。ストップウォッチは「計測中」「一時停止(ポーズ)」「リセット/終了」といった状態を持ち、ユーザが押すボタンによって状態が変わる典型的な状態機械です。
@startuml ストップウォッチ状態遷移
[*] --> 計測中 : Sボタン ' 開始状態から計測中へ(スタートボタン)
計測中 --> 一時停止 : Sボタン ' 計測中に再度スタートボタン(ポーズ)
一時停止 --> 計測中 : Sボタン ' ポーズ中にスタートボタン(再開)
一時停止 --> [*] : Rボタン ' ポーズ中にリセットボタン(終了)
@enduml
上図はストップウォッチの状態遷移を表したものです。最初、開始ノード[*]
から「計測中」状態に遷移しています。これはストップウォッチが初期状態(待機状態)から、ユーザの**Sボタン(スタートボタン)**操作によって計測を開始することを意味します。その後、計測中に再度Sボタンを押すと「一時停止」状態へ遷移し、さらにもう一度Sボタンを押すと「計測中」状態に戻っています。これは計測の一時停止と再開を表現しています。そして「一時停止」状態で Rボタン(リセットボタン) を押すと終了状態[*]
(停止)へ遷移し、計測が完全に終了する流れになっています。
この状態遷移図により、ストップウォッチの挙動を網羅的に理解できます。どのボタン操作でどの状態に移るのか、一目瞭然です。また、例えば「計測中」にRボタンを押すケース(この図では描かれていない)など、図にない遷移が発生した場合は何も起こらない(入力無視)か、あるいは操作自体を無効化する、といった扱いになることも読み取れます(状態遷移図では表現しない部分ですが、コード設計時に検討が必要なポイントです)。
Python視点で見る状態管理
続いて、このストップウォッチ状態遷移のロジックをPythonコード風に表現してみます。状態は文字列やEnumなどで管理し、ボタン押下イベントごとに状態を更新する実装です。
class Stopwatch:
def __init__(self):
# 初期状態を「停止中(Reset)」とする
self.state = "Reset"
def press_s(self):
# Sボタン押下時の状態遷移
if self.state == "Reset":
self.state = "Measuring" # 停止→計測開始
elif self.state == "Measuring":
self.state = "Paused" # 計測中→一時停止
elif self.state == "Paused":
self.state = "Measuring" # 一時停止→再開(計測中)
print(f"[Sボタン] 現在の状態: {self.state}")
def press_r(self):
# Rボタン押下時の状態遷移
if self.state == "Paused":
self.state = "Reset" # 一時停止→リセット(停止)
print(f"[Rボタン] 現在の状態: {self.state}")
# ストップウォッチ状態遷移のテスト
sw = Stopwatch()
sw.press_s() # 初期停止状態 -> 計測中
sw.press_s() # 計測中 -> 一時停止
sw.press_s() # 一時停止 -> 計測中(再開)
sw.press_r() # 計測中状態ではRボタン効かず(状態変化なし)
sw.press_s() # 計測中 -> 一時停止
sw.press_r() # 一時停止 -> 停止リセット
上記コードでは、Stopwatch
クラスが内部にself.state
を持ち、press_s()
メソッドとpress_r()
メソッドがそれぞれSボタン・Rボタン押下時の状態遷移を実装しています。初期状態は「Reset」(停止中)で、press_s()
が呼ばれるとReset -> Measuring
に状態が変わります(計測開始)。続けてもう一度press_s()
を呼ぶとMeasuring -> Paused
(一時停止)になり、さらにpress_s()
でPaused -> Measuring
(再開)となります。press_r()
は一時停止中(Paused)のときのみReset
状態に遷移させ、それ以外(計測中など)では状態を変えないようにしています。この挙動は前述の状態遷移図と一致しています。
実行結果のログ(コメントで示した状態変化)からも、PlantUMLで描いた図の通りに状態が遷移していることが確認できます。なお、実際の実装では計測中にRボタンが押された場合の扱い(このコードでは無視しています)や、エラー状態の追加など、状態遷移図に描かれていないケースも検討する必要があります。しかし、状態遷移図があることで 「実装すべき基本遷移」と「無視すべきイベント」 が明確になり、抜け漏れの少ない堅牢な設計につながります。
※補足:Pythonにはこのような状態遷移を扱うための便利なライブラリ(例えばtransitions
)も存在します。これを使うと、状態と遷移を定義するだけで上記と同等のステートマシン動作を実現でき、さらにPlantUML形式の状態図を自動生成する機能も提供されています。興味がある方は下記の記事などを参照してください。
PlantUML文法の具体例と図別の書き方のまとめ
アクティビティ図(処理フローを描く)
主な文法ルール
記法 | 意味 | 表示される図形 |
---|---|---|
start / stop
|
開始/終了 | ● → ● |
:処理; |
アクション | 長方形 |
if (...) then (...) / else (...) / endif
|
条件分岐 | ひし形 |
repeat / repeat while (...)
|
繰り返し | ループ構造 |
🔸 具体例:ログイン処理のフロー
@startuml
start
:ログイン画面を表示;
:ユーザーがIDとパスワードを入力;
if (認証成功?) then (yes)
:ホーム画面を表示;
else (no)
:エラーメッセージを表示;
endif
stop
@enduml
シーケンス図(時系列のやり取り)
主な文法ルール
記法 | 意味 |
---|---|
participant , actor
|
登場人物またはシステム |
A -> B: メッセージ |
通常の呼び出し |
A --> B: 応答 |
戻り(非同期) |
activate , deactivate
|
ライフラインの強調 |
具体例:WebアプリのAPI呼び出し
@startuml
actor User
participant "Web App" as Web
participant "DB Server" as DB
User -> Web: GET /user/123
activate Web
Web -> DB: SELECT * FROM users
activate DB
DB --> Web: ユーザーデータ
deactivate DB
Web --> User: JSONレスポンス
deactivate Web
@enduml
クラス図(オブジェクトの構造)
主な文法ルール
記法 | 意味 | |
---|---|---|
class クラス名 |
クラス定義 | |
+ , - , #
|
アクセス修飾子(public/private/protected) | |
extends / `< |
--` | 継承関係 |
具体例:動物と犬の継承
@startuml
class Animal {
+name: String
+speak(): void
}
class Dog extends Animal {
+breed: String
+speak(): void
}
Animal <|-- Dog
@enduml
ユースケース図(ユーザーと機能の関係)
主な文法ルール
記法 | 意味 |
---|---|
actor |
ユーザーや外部システム |
usecase |
機能 |
actor --> usecase |
利用関係 |
具体例:ショッピングサイト
@startuml
actor 顧客
actor 管理者
usecase "商品を検索" as UC1
usecase "商品を購入" as UC2
usecase "商品を登録" as UC3
顧客 --> UC1
顧客 --> UC2
管理者 --> UC3
@enduml
状態遷移図(状態の変化)
@startuml
[*] --> 待機中
待機中 --> 通知受信 : 新しいメール
通知受信 --> 閲覧中 : ユーザーがタップ
閲覧中 --> 待機中 : 閉じる
@enduml
🎨 カスタマイズ:色やレイアウトも指定可能
@startuml
class Animal {
+name: String
+speak(): void
}
class Dog extends Animal {
+breed: String
+speak(): void
}
Animal <|-- Dog
skinparam backgroundColor #F5F5DC
skinparam classAttributeIconSize 0
skinparam ArrowColor Blue
skinparam DefaultFontName "Helvetica"
@enduml
よく使うオプションまとめ
設定 | 内容 |
---|---|
skinparam |
見た目全体のカスタマイズ |
note right/left of |
各要素にコメントをつける |
group や rectangle
|
図中でグループ化・枠を付ける |
補足:PlantUMLの思想
- テキストで描く → Git管理しやすい
- 自動生成もできる(CIやドキュメント自動化)
- シンプルな構文で複雑な図も表現できる
機能のまとめ
- PlantUMLの構文はシンプルで直感的
- UMLの主要な図(アクティビティ・シーケンス・クラス・ユースケース)をすべてカバー
- GUIではなく「テキストで図を書く」ことで再利用性・履歴管理がしやすい
おわりに
本記事では、Pythonに馴染みのあるエンジニア向けに PlantUMLによるシーケンス図と状態遷移図の描き方 を解説しました。シーケンス図ではオブジェクト間のメッセージの流れを時間順に追い、状態遷移図ではシステムの状態変化を網羅的に整理できます。どちらも設計内容の理解を深め、ドキュメントとして残すのに非常に有用な手法です。