|
1 | 1 | --- |
2 | | -description: 階段 3 - 在定義的結構內實作邏輯(工程師角色) |
| 2 | +description: Phase 3 - 根據架構設計實作程式碼,滿足 Gherkin 規格 |
3 | 3 | --- |
4 | 4 |
|
5 | | -# SDD 階段 3:實作(血肉) |
| 5 | +# SDD Phase 3: 實作 |
6 | 6 |
|
7 | | -**角色:** 資深工程師 |
| 7 | +**角色:** 資深工程師 |
| 8 | +**輸入:** Gherkin 規格檔案 {{prompt}} |
| 9 | +**前置條件:** 已完成 Phase 2,存在 `docs/features/{feature_name}/architecture.md` |
| 10 | +**輸出:** 實作程式碼於專案既有目錄結構 |
8 | 11 |
|
9 | | -**目標:** 在定義的結構內實作業務邏輯,以滿足所有 Gherkin 情境。 |
| 12 | +## 核心原則 |
10 | 13 |
|
11 | | -## 輸入 |
| 14 | +- **遵循架構**:嚴格依照 architecture.md 定義的資料模型與服務介面 |
| 15 | +- **情境驅動**:每個 Gherkin 情境對應到程式碼邏輯分支 |
| 16 | +- **專案整合**:檔案放置於專案既有目錄結構 |
| 17 | +- **可測試性**:程式碼可直接用於驗證 Gherkin 情境 |
12 | 18 |
|
13 | | -提供兩個檔案: |
14 | | -1. Gherkin 規格:`features/<feature>.feature` |
15 | | -2. 結構定義:`structure/<feature>_structure.py`(或 `.ts`) |
| 19 | +## 執行步驟 |
16 | 20 |
|
17 | | -用法:`/sdd-impl features/<feature>.feature structure/<feature>_structure.py` |
| 21 | +### 1. 讀取架構設計 |
18 | 22 |
|
19 | | -提示:{{prompt}} |
| 23 | +從 `docs/features/{feature_name}/architecture.md` 讀取: |
| 24 | +- 專案上下文(語言、框架、架構模式) |
| 25 | +- 資料模型定義(列舉、實體) |
| 26 | +- 服務介面定義(方法簽名、參數、回傳值) |
| 27 | +- 情境對應關係 |
20 | 28 |
|
21 | | -## 您的職責約束 |
| 29 | +### 2. 實作元件 |
22 | 30 |
|
23 | | -- 您是資深工程師,實作嚴格定義的規格。 |
24 | | -- 您必須使用階段 2 的資料模型(結構檔案)。 |
25 | | -- 您必須實作階段 2 的所有介面。 |
26 | | -- 每個程式碼分支必須對應一個 Gherkin 情境。 |
27 | | -- 您的程式碼必須滿足 Gherkin 檔案中的所有情境。 |
28 | | -- 不要修改結構定義 - 只實作它們。 |
| 31 | +依據架構文件實作: |
29 | 32 |
|
30 | | -## 您的任務 |
| 33 | +**資料模型:** |
| 34 | +- 實作列舉/常數 |
| 35 | +- 實作核心實體 |
| 36 | +- 加入資料驗證(依框架) |
31 | 37 |
|
32 | | -1. **讀取兩個輸入檔案:** |
33 | | - - Gherkin 檔案(需求/測試規格) |
34 | | - - 結構檔案(資料模型和介面) |
| 38 | +**服務介面:** |
| 39 | +- 實作服務類別/介面 |
| 40 | +- 實作所有方法 |
| 41 | +- 每個方法對應 Gherkin 情境 |
35 | 42 |
|
36 | | -2. **建立具體實作:** |
37 | | - - 將每個介面實作為具體類別 |
38 | | - - 使用提供的資料模型 |
39 | | - - 將每個 Gherkin 情境對應到程式碼邏輯 |
| 43 | +**情境對應:** |
| 44 | +- `Given` → 設定/輸入參數 |
| 45 | +- `When` → 方法呼叫 |
| 46 | +- `Then` → 回傳值/驗證 |
40 | 47 |
|
41 | | -3. **實作對應:** |
42 | | - - `Given` 陳述 → 設定/輸入參數 |
43 | | - - `When` 陳述 → 方法呼叫/動作 |
44 | | - - `Then` 陳述 → 回傳值/結果 |
| 48 | +### 3. 儲存檔案 |
45 | 49 |
|
46 | | -4. **輸出格式:** |
| 50 | +依據 architecture.md 的「檔案結構」章節,將檔案放置於正確位置 |
47 | 51 |
|
48 | | -建立名為 `implementation/<feature_name>_impl.py`(或 `.ts`)的檔案: |
| 52 | +## 實作範例 |
49 | 53 |
|
50 | | -**Python 範例:** |
51 | | -```python |
52 | | -""" |
53 | | -<功能名稱> 的實作 |
54 | | -實作:structure/<feature_name>_structure.py |
55 | | -滿足:features/<feature_name>.feature |
56 | | -
|
57 | | -此模組包含結構模組中定義的 |
58 | | -服務介面的具體實作。 |
59 | | -""" |
| 54 | +### TypeScript + NestJS |
60 | 55 |
|
61 | | -from structure.<feature_name>_structure import * |
62 | | - |
63 | | -class <Service>Service(I<Service>Service): |
64 | | - """ |
65 | | - I<Service>Service 的具體實作。 |
66 | | - 實作 features/<feature_name>.feature 的所有情境 |
67 | | - """ |
| 56 | +**資料模型 (src/models/UserType.ts):** |
| 57 | +```typescript |
| 58 | +export enum UserType { |
| 59 | + VIP = 'VIP', |
| 60 | + NORMAL = 'NORMAL', |
| 61 | +} |
68 | 62 |
|
69 | | - def action_name(self, param1: Type1, param2: Type2) -> ResultType: |
70 | | - """ |
71 | | - 實作滿足: |
72 | | - - 情境:"<情境 1 名稱>" |
73 | | - - 情境:"<情境 2 名稱>" |
74 | | - """ |
75 | | - |
76 | | - # 將 Gherkin Given 條件對應到程式碼邏輯 |
77 | | - if condition_from_scenario_1: |
78 | | - # 將 Gherkin Then 對應到回傳值 |
79 | | - return result_for_scenario_1 |
80 | | - |
81 | | - # 處理情境 2 的邊界情況 |
82 | | - elif condition_from_scenario_2: |
83 | | - return result_for_scenario_2 |
84 | | - |
85 | | - # 處理情境 3 的錯誤情況 |
86 | | - if invalid_condition: |
87 | | - return ResultType( |
88 | | - success=False, |
89 | | - error_message="來自 Gherkin 的錯誤訊息" |
90 | | - ) |
91 | | - |
92 | | - # 預設/正常路徑 |
93 | | - return default_result |
94 | | - |
95 | | -# 範例用法(可選,用於示範) |
96 | | -if __name__ == "__main__": |
97 | | - service = <Service>Service() |
98 | | - |
99 | | - # 測試 Gherkin 的情境 1 |
100 | | - result = service.action_name(param1_value, param2_value) |
101 | | - print(f"結果:{result}") |
| 63 | +export interface User { |
| 64 | + id: string; |
| 65 | + userType: UserType; |
| 66 | + points: number; |
| 67 | +} |
102 | 68 | ``` |
103 | 69 |
|
104 | | -**TypeScript 範例:** |
| 70 | +**服務實作 (src/services/DiscountService.ts):** |
105 | 71 | ```typescript |
106 | | -/** |
107 | | - * <功能名稱> 的實作 |
108 | | - * 實作:structure/<feature_name>_structure.ts |
109 | | - * 滿足:features/<feature_name>.feature |
110 | | - */ |
111 | | - |
112 | | -import { |
113 | | - I<Service>Service, |
114 | | - <Entity>, |
115 | | - <Result>, |
116 | | - <EntityType> |
117 | | -} from './structure/<feature_name>_structure'; |
118 | | - |
119 | | -export class <Service>Service implements I<Service>Service { |
| 72 | +import { Injectable } from '@nestjs/common'; |
| 73 | +import { User, UserType } from '../models/UserType'; |
| 74 | + |
| 75 | +@Injectable() |
| 76 | +export class DiscountService { |
120 | 77 | /** |
121 | | - * 實作滿足: |
122 | | - * - 情境:"<情境 1 名稱>" |
123 | | - * - 情境:"<情境 2 名稱>" |
| 78 | + * 滿足情境: |
| 79 | + * - "VIP 使用者享有 20% 折扣" (第 5-8 行) |
| 80 | + * - "一般使用者無折扣" (第 10-13 行) |
124 | 81 | */ |
125 | | - actionName(param1: Type1, param2: Type2): ResultType { |
126 | | - // 將 Gherkin Given 條件對應到程式碼邏輯 |
127 | | - if (conditionFromScenario1) { |
128 | | - // 將 Gherkin Then 對應到回傳值 |
129 | | - return resultForScenario1; |
130 | | - } |
131 | | - |
132 | | - // 處理情境 2 的邊界情況 |
133 | | - if (conditionFromScenario2) { |
134 | | - return resultForScenario2; |
| 82 | + calculateDiscount(user: User, amount: number): number { |
| 83 | + // Given: 使用者是 VIP (情境 1) |
| 84 | + if (user.userType === UserType.VIP) { |
| 85 | + // Then: 折扣 20% |
| 86 | + return amount * 0.8; |
135 | 87 | } |
136 | 88 |
|
137 | | - // 處理情境 3 的錯誤情況 |
138 | | - if (invalidCondition) { |
139 | | - return { |
140 | | - success: false, |
141 | | - errorMessage: "來自 Gherkin 的錯誤訊息" |
142 | | - }; |
143 | | - } |
144 | | - |
145 | | - // 預設/正常路徑 |
146 | | - return defaultResult; |
| 89 | + // Given: 使用者是 NORMAL (情境 2) |
| 90 | + // Then: 無折扣 |
| 91 | + return amount; |
147 | 92 | } |
148 | 93 | } |
149 | 94 | ``` |
150 | 95 |
|
151 | | -## 完整範例 |
152 | | - |
153 | | -**Gherkin(features/vip_discount.feature):** |
154 | | -```gherkin |
155 | | -Feature: VIP 折扣 |
156 | | - Scenario: 對 VIP 使用者套用折扣 |
157 | | - Given 使用者是 VIP |
158 | | - When 使用者購買 1000 美元 |
159 | | - Then 最終價格應該是 800 美元 |
160 | | -
|
161 | | - Scenario: 非 VIP 使用者無折扣 |
162 | | - Given 使用者是 NORMAL |
163 | | - When 使用者購買 1000 美元 |
164 | | - Then 最終價格應該是 1000 美元 |
165 | | -``` |
| 96 | +### Python + FastAPI |
166 | 97 |
|
167 | | -**結構(structure/vip_discount_structure.py):** |
| 98 | +**資料模型 (src/models/user.py):** |
168 | 99 | ```python |
| 100 | +from enum import Enum |
| 101 | +from pydantic import BaseModel |
| 102 | + |
169 | 103 | class UserType(str, Enum): |
170 | 104 | VIP = "VIP" |
171 | 105 | NORMAL = "NORMAL" |
172 | 106 |
|
173 | | -class IDiscountService(ABC): |
174 | | - @abstractmethod |
175 | | - def calculate_discount(self, amount: float, user_type: UserType) -> float: |
176 | | - pass |
| 107 | +class User(BaseModel): |
| 108 | + id: str |
| 109 | + user_type: UserType |
| 110 | + points: int = 0 |
177 | 111 | ``` |
178 | 112 |
|
179 | | -**實作(implementation/vip_discount_impl.py):** |
| 113 | +**服務實作 (src/services/discount_service.py):** |
180 | 114 | ```python |
181 | | -from structure.vip_discount_structure import * |
182 | | - |
183 | | -class DiscountService(IDiscountService): |
184 | | - """實作 VIP 折扣邏輯。""" |
185 | | - |
186 | | - def calculate_discount(self, amount: float, user_type: UserType) -> float: |
187 | | - """ |
188 | | - 滿足情境: |
189 | | - - "對 VIP 使用者套用折扣" |
190 | | - - "非 VIP 使用者無折扣" |
191 | | - """ |
192 | | - # 情境 1:VIP 享 20% 折扣 |
193 | | - if user_type == UserType.VIP: |
194 | | - return amount * 0.8 |
| 115 | +from src.models.user import User, UserType |
195 | 116 |
|
196 | | - # 情境 2:非 VIP 支付全額 |
| 117 | +class DiscountService: |
| 118 | + """ |
| 119 | + 滿足情境: |
| 120 | + - "VIP 使用者享有 20% 折扣" (第 5-8 行) |
| 121 | + - "一般使用者無折扣" (第 10-13 行) |
| 122 | + """ |
| 123 | + def calculate_discount(self, user: User, amount: float) -> float: |
| 124 | + # Given: 使用者是 VIP (情境 1) |
| 125 | + if user.user_type == UserType.VIP: |
| 126 | + # Then: 折扣 20% |
| 127 | + return amount * 0.8 |
| 128 | + |
| 129 | + # Given: 使用者是 NORMAL (情境 2) |
| 130 | + # Then: 無折扣 |
197 | 131 | return amount |
| 132 | +``` |
198 | 133 |
|
199 | | -# 驗證 |
200 | | -if __name__ == "__main__": |
201 | | - service = DiscountService() |
| 134 | +## 程式碼撰寫要求 |
202 | 135 |
|
203 | | - # 測試情境 1 |
204 | | - assert service.calculate_discount(1000, UserType.VIP) == 800 |
| 136 | +### 註解標註 |
| 137 | +- 每個方法標註對應的 Gherkin 情境與行數 |
| 138 | +- 關鍵邏輯註明對應的 Given/When/Then |
205 | 139 |
|
206 | | - # 測試情境 2 |
207 | | - assert service.calculate_discount(1000, UserType.NORMAL) == 1000 |
| 140 | +### 錯誤處理 |
| 141 | +依據專案既有模式處理異常: |
| 142 | +```typescript |
| 143 | +// TypeScript |
| 144 | +if (!user) { |
| 145 | + throw new BadRequestException('使用者不存在'); |
| 146 | +} |
| 147 | +``` |
208 | 148 |
|
209 | | - print("✅ 所有情境驗證成功") |
| 149 | +```python |
| 150 | +# Python |
| 151 | +if not user: |
| 152 | + raise ValueError("使用者不存在") |
210 | 153 | ``` |
211 | 154 |
|
212 | | -## 品質檢查清單 |
| 155 | +### 驗證邏輯 |
| 156 | +依據框架加入資料驗證(如需要) |
213 | 157 |
|
214 | | -完成前,確保: |
215 | | -- [ ] 結構檔案中的所有介面都已實作 |
216 | | -- [ ] 結構檔案中的所有資料模型都已使用 |
217 | | -- [ ] 每個 Gherkin 情境都有對應的程式碼邏輯 |
218 | | -- [ ] 沒有修改結構定義 |
219 | | -- [ ] 程式碼包含對應到 Gherkin 情境的註解 |
220 | | -- [ ] 包含基本驗證/測試程式碼(可選但建議) |
221 | | -- [ ] 檔案已儲存在 `implementation/` 目錄 |
| 158 | +## 品質檢查 |
222 | 159 |
|
223 | | -## 下一步 |
| 160 | +- [ ] 已讀取 `docs/features/{feature_name}/architecture.md` |
| 161 | +- [ ] 所有資料模型已實作 |
| 162 | +- [ ] 所有服務介面已實作 |
| 163 | +- [ ] 每個 Gherkin 情境都有對應程式碼邏輯 |
| 164 | +- [ ] 程式碼符合專案既有命名慣例與架構模式 |
| 165 | +- [ ] 檔案儲存至正確位置(依 architecture.md) |
| 166 | +- [ ] 包含 Gherkin 情境追溯註解 |
224 | 167 |
|
225 | | -完成此階段後: |
226 | | -- 儲存實作檔案 |
227 | | -- 使用以下指令進入階段 4:`/sdd-verify features/<feature>.feature implementation/<feature>_impl.py` |
228 | | -- 或在驗證前執行手動測試 |
| 168 | +## 執行流程 |
| 169 | + |
| 170 | +1. 讀取 `docs/features/{feature_name}/architecture.md` |
| 171 | +2. 讀取 `features/{feature_name}.feature` |
| 172 | +3. 依據架構文件與專案上下文實作程式碼 |
| 173 | +4. 將檔案儲存至專案既有目錄結構 |
| 174 | +5. 回報已建立的檔案清單 |
| 175 | + |
| 176 | +## 下一步 |
229 | 177 |
|
230 | | -現在讀取兩個輸入檔案並建立實作。 |
| 178 | +完成後可進行: |
| 179 | +- 執行專案既有測試框架驗證 |
| 180 | +- 進入 Phase 4:測試驗證(如有定義) |
| 181 | +- 整合至專案主要程式碼 |
0 commit comments