四步 OOA 工作流
這是標準工作流。按順序進行。別跳過步驟。別合併步驟。
步驟 1:領域詞彙與關係
目標
萃取並命名領域的核心概念,並闡明它們如何關聯。
要產出什麼
-
詞彙表,依概念類型分組。常見分組:
- 主體類(Entities) — 行為者、具識別的事物
- 時間類(Temporal) — 班表、時間窗、週期
- 能力類(Capabilities) — 實體能做什麼
- 匹配類(Rules / Matching) — 連結概念的業務規則
- 查詢類(Query views) — 唯讀投影
- 操作關係類(Operating relations) — 連結多個實體的依賴
-
關係草圖,用純文字/箭頭(先別用 Mermaid)。標出方向。例如:
Resource ──belongs to──> WorkCenter Resource ──has 1──> Schedule ──composed of──> Shift[] -
值得早點提出的設計分歧 — 關係方向、繼承 vs. 組合、某概念「住」在哪(哪個 package/aggregate)。
關鍵行為
- 先用使用者的詞彙。 若他說「資源」(resource),別默默改名成「Entity」。他的用字帶有領域意義。
- 雙語名詞:若使用者用中文書寫,中文 + 英文並列(例如:班表 / Schedule)。這有助於之後的 Java 識別碼命名。
- 別引入使用者沒提到的概念,除非你明白標出來:「我建議也引入
OperatingRelation來表達 X——這符合你的心智模型嗎?」 - 攤開使用者描述留下的模糊。 常見模糊:
- X 是 entity 還是 Value Object?
- X 由 Y 擁有,還是只是與 Y 關聯?
- X 有識別,還是可互換?
結束步驟 1
以 2-3 個針對性分歧作結,讓使用者拍板。別問「還有別的嗎?」——問具體問題。多選分歧用 ask_user_input_v0。例如:
- 「Resource 該用繼承(Machine/Mold/Worker 子類別)還是組合(Resource 帶一個
type欄位 + 行為 strategy)?」 - 「自動化程度是 Machine 的屬性,還是其實是該住在別處的排程關注點?」
- 「匹配該由需求端驅動(PartNumber → 所需資源),還是供給端驅動(Resource → 它能做什麼)?」
步驟 2:類別圖(Mermaid)
目標
把詞彙轉譯成帶屬性、方法與關係的視覺化類別結構。
要產出什麼
一個 Mermaid classDiagram 區塊,顯示:
- 步驟 1 的所有核心類別
- 帶型別的屬性(用
+public、-private、#protected——但 OOA 階段偏好全用-;我們還沒在指定 API) - 關鍵方法(只要簽名,不要方法體)
- 帶多重性與方向的關係
- 適當處的列舉(用
<<enumeration>>) - 介面用
<<interface>>、抽象類別用<<abstract>>
須遵循的 Mermaid 慣例
classDiagram direction TB class ClassName { <<abstract>> %% 或 <<interface>> 或 <<enumeration>> -fieldName: FieldType +methodName(param) ReturnType } %% 關係: ClassA <|-- ClassB %% B 繼承 A InterfaceA <|.. ClassB %% B 實作 A ClassA "1" *-- "*" ClassB %% A 由多個 B 組成(強擁有) ClassA "1" o-- "*" ClassB %% A 聚合多個 B(弱擁有) ClassA "*" --> "1" ClassB %% A 與 B 關聯
方向指引:
- 多數圖用
TB(上到下) - 鏈狀或序列用
LR(左到右)
用 %% ========== 群組名 ========== 註解把相關類別分組。這讓大圖更易讀。
須避免的常見陷阱
- 別顯示每個 getter/setter。 OOA 階段的方法是查詢與關鍵命令。存取器留到實作階段。
- 別任意決定關係方向。 方向編碼了擁有與依賴——要弄對。
- 別加沒有目的的欄位/方法。 若你解釋不了某方法為何存在,就刪掉。
- 別混用抽象層次。 別把
equals()放在findCandidates()旁邊。前者是實作,後者是 domain。
子圖
對複雜系統,在完整圖之後,加 1-2 張聚焦的子圖,單獨呈現關鍵子系統。例如:
- 完整圖:30 個類別
- 子圖 1:行事曆子系統(5 個類別,放大)
- 子圖 2:Specification 樹(4 個類別,呈現組合)
結束步驟 2
別自動推進到步驟 3。問:
- 「這張圖符合你的心智模型嗎?」
- 「有什麼要增、刪、或重塑的嗎?」
在前進前,等待確認或回饋。
步驟 3:SOLID 審查 + 設計模式
目標
就 SOLID 合規性稽核設計,並在徵得使用者同意下提出具體設計模式。
要產出什麼
部分 A:SOLID 檢查清單
對每個原則(SRP、OCP、LSP、ISP、DIP),說明:
- ✅ 設計在哪裡滿足它 — 從圖中舉具體例子
- ⚠️ 潛在違反或風險 — 以及哪些徵兆日後會浮現它們
- 💡 若存在違反的重構建議
別只說「符合 SRP」——點名哪個類別、為什麼。例如:
✅ SRP:
Schedule只處理時間(baseline + 例外組合);Capability只處理一個資源能做什麼。
⚠️ ISP 風險:對
Worker而言,Resource.hasCapability(Process)感覺很怪——工人是以技能在思考,不是以工序。要嘛改名,要嘛只為 Machine/Mold 萃取一個ProcessExecutor介面。
部分 B:設計模式提案
對每個能改善設計的模式,以表格呈現:
| # | 模式 | 用於 | 必要性 |
|---|---|---|---|
| 1 | Specification | 組合匹配規則 | 🔥 強烈推薦 |
| 2 | Repository | 解耦資料存取 | ✅ DDD 標準 |
| 3 | Strategy | 各型別行為不同 | ⭐ 選用 |
必要性圖例:
- 🔥 強烈推薦 — 沒有它設計會有真實痛點
- ✅ 標準做法 — 自然契合、成本低
- ⭐ 選用 — 有用但增加複雜度,到需要時再做
採用前先問。 用 ask_user_input_v0:
「要我納入哪些模式?(a) 全部,(b) 只要 Specification + Repository,(c) 維持現狀不用模式。」
詳細模式指引,請讀 design-principles.md。
關鍵行為
- 別提設計不需要的模式。 模式上癮(pattern-itis)是真的。若不用 Strategy 也能滿足 SRP,就別引入 Strategy。
- 解釋後果。 「在這裡加 Composite 代表⋯」——使用者得到什麼、付出什麼?
- 承接使用者先前說過的話。 若使用者已決定把某關注點移到別處(例如「AutomationLevel 是排程關注點,不是資源關注點」),在審查中反映那點。
結束步驟 3
更新類別圖(若採用了模式)並呈現修訂版。然後問是否要:
- (a) 進到步驟 4(程式碼骨架)
- (b) 繼續疊代這張圖
- (c) 產出文件(給 Claude Code 或團隊交接的 Markdown 檔)
- (d) 到此為止——設計已足夠好,使用者可自行實作
步驟 4:程式碼骨架 + 測試 (選用,僅在要求時)
目標
產出帶代表性測試的 Java 介面與基底類別骨架,作為實作的起點。
開始前先確認:
- 程式碼風格(用
ask_user_input_v0):- 純 POJO(無框架)?Spring Boot?Java record?Lombok?
- 細節程度:
- 核心類別 + 介面 + 2-3 個代表性測試(輕量)?或
- 含全部類別的完整可編譯骨架?
- 註解語言:
- 英文?繁體中文?混用(程式碼英文、註解中文)?
這三個答案形塑整份輸出。別假設——一律先問。
輸出結構
- Package 佈局 — 顯示提議的目錄樹
- 核心類別,依 package 分組、按依賴順序排列(shared → organization → domain → application)
- 測試類別 — 2-3 個涵蓋最棘手不變式的代表性測試
風格慣例
欄位宣告
除非另有要求,OOA 階段的骨架別產生 getter/setter。用註解標示欄位:
public class RecurringShift {
// 欄位:
// String shiftId
// DayOfWeek dayOfWeek
// LocalTime startTime
// Duration duration
public boolean isOvernight() { ... }
public List<TimeSlot> materialize(LocalDate date) { ... }
}這讓焦點放在結構與關係上,而非樣板程式。
方法體
三種風格,依脈絡選擇:
- Stub(
{ ... }):用於圖已傳達意圖的例行方法 - 虛擬碼註解:用於有微妙順序或不變式的方法
- 真實實作:用於展示核心設計理念的方法(例如某模式的主力方法)
測試
- 預設用 JUnit 5
- 測試名稱:英文
should...形式 - 測試內部:用使用者偏好語言寫註解
- 一個測試對應一個不變式或邊界案例,而非對應一個方法
須遵循的常見樣式
讀 output-templates.md 取得下列項目的標準範例:
- Java 版的 Specification 模式
- Repository 介面
- 帶前置條件檢查的 Aggregate Root
- 帶 factory method 的不可變 Value Object
- 帶 template method 的繼承
結束步驟 4
提供後續路徑:
- 把 stub 展開成完整實作
- 加更多測試(指定哪些不變式)
- 產出給 Claude Code 的 Markdown 文件
- 進到 OOD 或實作階段