🎯 概覽
本指南將引導您完成電話呼叫控制系統的設計與建模,使用UML 狀態機圖。本指南著重於外撥呼叫的生命週期,說明電話線如何根據使用者操作與網路事件在不同狀態之間轉換。
該圖表涵蓋了正常路徑(呼叫設定成功)與異常路徑(錯誤、超時、佔線),強調魯棒性、例外處理與清晰的狀態轉換——這些是即時通訊系統的核心原則。
🧩 UML 狀態機的核心概念
在深入圖表之前,請先理解這些 UML 基礎概念:
| 概念 | 說明 |
|---|---|
| 狀態 | 物件滿足特定條件或執行特定動作的狀態。 |
| 轉換 | 由事件觸發的從一個狀態到另一個狀態的變化。 |
| 事件 | 導致轉換的發生(例如:掛機, 有效號碼). |
| 自我轉換 | 起始與終止於同一狀態的轉換(例如:digit(n) 在…期間 撥號). |
| 偽狀態 | 特殊控制點,例如 初始 或 終止 並非實際狀態。 |
| 複合狀態 | 包含子狀態的狀態(例如 錯誤 狀態,其中包含 忙音, 快速忙音, 錄製訊息). |
| 保護條件 | 一個布林表達式,必須為真才能發生轉換。 |
✅ 專業提示: 使用
event [guard] / action語法在 UML 中記錄觸發器、條件和副作用。
🔄 外撥通話生命週期:逐步分解
1. 啟動與撥號階段
🔹 初始偽狀態 → 閒置
- 系統起始於初始偽狀態.
- 尚未有任何活動;電話處於掛機狀態。
🔹 閒置 → 撥號音 (onHook)
- 事件:
onHook(用戶拿起聽筒) - 轉換:
onHook → DialTone - 動作:產生撥號音;準備輸入數字。
📌 這是通話生命週期中第一個可見的狀態變更。
🔹 撥號音 → 撥號中 (digit(n))
- 事件:
digit(n)(用戶輸入一個數字) - 轉換:
digit(n) → Dialing - 狀態:進入
撥號中模式。
🔹 自我轉換:撥號中 → 撥號中 (digit(n))
- 事件:
digit(n)(輸入多個數字) - 守衛: 無(始終允許)
- 動作: 將數字追加至正在撥打的號碼。
- 目的: 允許在不離開「
撥號」」狀態下持續輸入數字。
💡 自我轉換對於處理如電話號碼等輸入序列至關重要。
2. 連線邏輯與例外處理
🔹 撥號 → 連線(validNumber)
- 事件:
validNumber(號碼已完整驗證) - 轉換:
validNumber → 連線 - 動作: 與網路啟動通話設定。
🔹 撥號 → 播放預錄訊息(invalidNumber)
- 事件:
invalidNumber(例如:長度錯誤、前綴無效) - 轉換:
invalidNumber → 播放預錄訊息 - 動作: 播放預錄訊息:「您撥打的號碼無法使用。」
🔹 連線 → 忙音(numberBusy)
- 事件:
號碼忙線 - 轉換:
numberBusy → 忙音 - 動作:播放忙音;通知用戶該線路已被佔用。
🔹 連接中 → 快速忙音 (中繼線忙)
- 事件:
中繼線忙 - 轉換:
trunkBusy → 快速忙音 - 動作:播放快速忙音;表示網路擁塞。
⚠️ 注意:這些是錯誤狀態會中斷正常流程。必須以妥善方式處理。
3. 超時與警告機制
🔹 撥號中 → 警告 (超時)
- 事件:
超時在 30 秒無活動後 - 轉換:
timeout → 警告 - 動作:播放警告嗶聲;通知用戶繼續操作或掛斷。
🔹 警告 → 超時 (超時)
- 事件:
超時10 秒後再次 - 轉換:
超時 → 超時 - 動作:取消呼叫嘗試;返回至
閒置.
⏱️ 超時邏輯可防止無限等待並提升使用者體驗。
4. 通話中與斷線
🔹 連接中 → 振鈴(已路由)
- 事件:
已路由(網路成功路由該通話) - 轉換:
已路由 → 振鈴 - 動作:向被叫方發送振鈴訊號。
🔹 振鈴 → 已連線(被叫方接聽)
- 事件:
被叫方接聽 - 轉換:
被叫方接聽 → 已連線 - 動作:建立音訊連線;開始通話錄音(若已啟用)。
🔹 已連線 → 已斷線(掛上聽筒 或 被叫方掛斷)
- 兩種斷線途徑:
- 使用者掛斷:
掛上聽筒 → 已斷線 - 對方掛斷:
calledPhoneHangsUp → 已斷開
- 使用者掛斷:
🔄 兩種轉換均指向
已斷開之後才到達最終狀態.
🔹 已斷開 → 最終狀態
- 事件: 無(隱含或透過清理動作)
- 轉換:
已斷開 → 最終 - 動作: 清理資源、記錄通話時長、更新統計數據。
✅ 最終狀態代表通話生命週期的結束。
🎨 用於清晰度的視覺設計原則
為了讓複雜的狀態機易於閱讀與維護:
| 原則 | 實作 |
|---|---|
| 主要成功路徑置於中央 | 將主要流程(閒置 → 撥號音 → 撥號中 → 連接中 → 振鈴 → 已接通)保持為一條清晰的垂直或水平線。 |
| 例外情況向外分支 | 將錯誤狀態(忙音、快速忙音、錄製訊息)作為側邊分支放置。 |
| 將相關狀態分組 | 使用複合狀態 來處理錯誤條件(見下文)。 |
| 謹慎使用偽狀態 | 初始 和 最終 應清楚標示。 |
| 避免交叉轉換 | 避免箭頭重疊;如有需要,請使用正交區域。 |
🔧 進階建模技術
✅ 複合狀態:「錯誤」分組
與其列出 忙音, 快速忙音、以及 錄製訊息 作為獨立狀態,請將它們歸類至一個 複合狀態 名為 錯誤:
[錯誤]
├── 忙音
├── 快速忙音
└── 錄製訊息
- 進入動作: 播放錯誤音效或訊息。
- 退出動作: 返回至
撥號音或閒置於使用者回應後。
✅ 效益:減少視覺雜訊並提升可擴展性。
✅ 保護條件(可選增強)
新增保護條件以精細化狀態轉換:
digit(n) [number.length < 15] → 撥號中
validNumber [number.isInternational] → 連線中
🛠️ 保護條件可防止無效的狀態轉換並支援條件邏輯。
📌 重點摘要:複雜狀態機的最佳實踐
| 實作 | 為何重要 |
|---|---|
| 模擬不順暢路徑 | 真實系統會失敗。設計時應考慮 無效號碼, 逾時, 線路繁忙可確保可靠性。 |
| 使用動作表達式 | 包含 / logCallAttempt()或 / playTone()以顯示副作用。 |
| 保持事件描述詳盡且以動作為導向 | 使用 onHook, routed, calledPhoneAnswers 而非 e1, e2. |
| 明確命名狀態 | 避免 State1, State2。使用 撥號中, 振鈴中, 已連線. |
| 記錄假設條件 | 例如,「30 秒無活動後超時」應在註解中註明。 |
💻 程式碼產生:PlantUML 與 Mermaid
以下是 即插即用的程式碼區塊 以您偏好的格式產生此圖表。
✅ PlantUML 程式碼
@startuml
[*] --> Idle
Idle --> DialTone : onHook
DialTone --> Dialing : digit(n)
Dialing --> Dialing : digit(n) ' 自我轉換
Dialing --> Connecting : validNumber
Dialing --> RecordedMessage : invalidNumber
Dialing --> Warning : timeout
Warning --> Timeout : timeout
Connecting --> Ringing : routed
Connecting --> BusyTone : numberBusy
Connecting --> FastBusyTone : trunkBusy
Ringing --> Connected : calledPhoneAnswers
Connected --> Disconnected : onHook
Connected --> Disconnected : calledPhoneHangsUp
Disconnected --> [*] : cleanup
state "Error" as ErrorState {
state "BusyTone" as BusyTone
state "FastBusyTone" as FastBusyTone
state "RecordedMessage" as RecordedMessage
}
' 內部動作
Idle : entry / 等待摘機
DialTone : entry / 播放撥號音
Dialing : entry / 收集數字
Connecting : entry / 路由通話
Ringing : entry / 振鈴遠端電話
Connected : entry / 建立通話會話
Disconnected : entry / 終止會話
@enduml 📥 如何使用: 貼上至 PlantUML 線上版 或您的 IDE 外掛程式。
✅ Mermaid 程式碼

stateDiagram-v2
[*] --> Idle
Idle --> DialTone : onHook
DialTone --> Dialing : digit(n)
Dialing --> Dialing : digit(n) ' 自我轉換
Dialing --> Connecting : validNumber
Dialing --> RecordedMessage : invalidNumber
Dialing --> Warning : timeout
Warning --> Timeout : timeout
Connecting --> Ringing : routed
Connecting --> BusyTone : numberBusy
Connecting --> FastBusyTone : trunkBusy
Ringing --> Connected : calledPhoneAnswers
Connected --> Disconnected : onHook
Connected --> Disconnected : calledPhoneHangsUp
Disconnected --> [*] : cleanup
state Error {
BusyTone
FastBusyTone
RecordedMessage
}
Connecting --> BusyTone : numberBusy
Connecting --> FastBusyTone : trunkBusy
Dialing --> RecordedMessage : invalidNumber
note right of BusyTone
播放標準忙音
end note
note right of FastBusyTone
播放快速忙音(網路擁塞)
end note
note right of RecordedMessage
播放錄音訊息:「該號碼未服務。」
end note
note right of Timeout
40 秒後取消呼叫嘗試
end note
📥 使用方法: 貼上至 Mermaid 線上編輯器 或支援的 Markdown 工具(VS Code、Obsidian 等)。
📚 摘要與總結
此 電話呼叫控制系統 狀態機是 一個真實世界的範例 展示 UML 如何以高可靠性建模複雜且事件驅動的系统。
✅ 此圖表有效的關鍵:
- 清晰的 成功路徑 且邏輯流暢。
- 全面的 錯誤處理.
- 運用 自我轉換, 複合狀態,以及 守衛條件.
- 透過 分組 以及 註解.
🛠️ 何時使用此模式:
- 電話系統
- 物聯網裝置控制
- 使用者會話管理
- 工作流程引擎
- 具有有限狀態邏輯的嵌入式系統
📝 想要擴充此功能嗎?
考慮新增:
- 通話錄音 狀態(包含
startRecording,stopRecording事件) - 通話轉接 邏輯(條件式路由)
- 來電等待 支援(平行狀態)
- 通話轉移 作為
已連線 - 狀態歷史(淺層/深層歷史)用於中斷後重新進入
📌 最終建議
務必同時建模成功路徑與失敗路徑。
僅處理「順利路徑」的狀態機是不完整的,且在生產環境中容易出現錯誤。
將本指南作為範本來建模任何實時系統,其中狀態轉換, 事件,以及錯誤韌性至關重要。
✅ 準備好生成、可視化或擴充了嗎?
👉 複製以下PlantUML或Mermaid程式碼,並將其整合至您的文件、架構圖或系統設計文件中。
如果您需要PDF 版本, 互動式圖表,或整合至更大的系統模型(例如,包含元件或序列圖)!
📘 「最佳系統不僅正確無誤,更能預見失敗。」
— 使用 UML 狀態機進行設計












