对应源码: conversation.rs:100-110 pub struct ConversationRuntime { api_client: C, ← 从外部传入 tool_executor: T, ← 从外部传入 permission_policy: ..., ← 从外部传入 hook_runner: ..., ← 从外部传入 } 112-116: im
()
| 177 | # ============================================================ |
| 178 | |
| 179 | def lesson_2_dependency_injection(): |
| 180 | """ |
| 181 | 对应源码: |
| 182 | conversation.rs:100-110 |
| 183 | pub struct ConversationRuntime<C, T> { |
| 184 | api_client: C, ← 从外部传入 |
| 185 | tool_executor: T, ← 从外部传入 |
| 186 | permission_policy: ..., ← 从外部传入 |
| 187 | hook_runner: ..., ← 从外部传入 |
| 188 | } |
| 189 | |
| 190 | 112-116: |
| 191 | impl<C, T> ConversationRuntime<C, T> |
| 192 | where |
| 193 | C: ApiClient, ← 只要求实现 ApiClient 接口 |
| 194 | T: ToolExecutor, ← 只要求实现 ToolExecutor 接口 |
| 195 | |
| 196 | "依赖注入" 听起来很可怕, 其实就是: |
| 197 | 不要在内部 new, 而是从外部传进来。 |
| 198 | |
| 199 | 日常类比: 手机壳 |
| 200 | ───────────── |
| 201 | 手机不会在出厂时焊死一个壳。 |
| 202 | 你从外面套上去——想换透明的? 硅胶的? 皮革的? 随便换。 |
| 203 | "手机" = ConversationRuntime, "手机壳" = ApiClient/ToolExecutor。 |
| 204 | """ |
| 205 | print("=" * 60) |
| 206 | print("第二课: 依赖注入 — 插座: 从外部传入, 不在内部写死") |
| 207 | print("=" * 60) |
| 208 | |
| 209 | # ---- 反面教材: 写死依赖 (不要这样!) ---- |
| 210 | print() |
| 211 | print(" 反面教材 (耦合):") |
| 212 | print(" ──────────────") |
| 213 | |
| 214 | class BadRuntime: |
| 215 | """糟糕的设计: API 客户端在内部写死""" |
| 216 | def __init__(self): |
| 217 | # 问题: 测试时怎么办? 没网络怎么办? 想换 API 怎么办? |
| 218 | import urllib.request # noqa: F401 — 演示"写死" |
| 219 | self.api_url = "https://api.anthropic.com/v1/messages" |
| 220 | |
| 221 | def run(self, user_input: str): |
| 222 | # 这里直接调 API——无法测试、无法替换 |
| 223 | pass |
| 224 | |
| 225 | print(" class BadRuntime:") |
| 226 | print(" def __init__(self):") |
| 227 | print(" self.api_url = 'https://api.anthropic.com/...' # 写死!") |
| 228 | print(" 问题: 测试? 没网? 换服务? 全部改代码!") |
| 229 | print() |
| 230 | |
| 231 | # ---- 正面教材: 依赖注入 ---- |
| 232 | print(" 正面教材 (注入):") |
| 233 | print(" ──────────────") |
| 234 | |
| 235 | class ApiClient(ABC): |
| 236 | @abstractmethod |
no test coverage detected