模块二-架构基础功 | 第08讲:SOLID 原则的 AI 时代新解读 - 为什么 AI 生成的代码总是违反开闭原则
开场:当「能跑」不再是标准,SOLID 变成你与 AI 的「共同语言」
如果你让大模型「帮我写一个审核服务」,它往往会在几十秒内给你一份看起来完整的实现:类名工整、注释礼貌、甚至附带 pytest。但当你把这份代码放进 CodeSentinel 的真实演进里,你会很快遇到一种熟悉的痛:每新增一种审核策略,就要改同一个文件;每接入一个新规则源,就要在核心类里再加一串 if/else;每换一个 LLM 提供商,业务层突然开始 import openai。
这不是你「不会提示工程」,而是生成式模型在统计上偏好最短路径补丁:它会把当下上下文里所有条件揉进一个函数,把具体实现直接写在调用方旁边,把「先交付」置于「可扩展」之上。于是 SOLID 不再是教科书里的五个缩写,而变成你与团队、与 AI、与门禁工具之间的硬约束语言:S 让你拒绝上帝类;O 让你用扩展点替代改源码;L 让你警惕继承陷阱;I 让你拆分臃肿协议;D 让你把具体技术细节赶到外层。
本讲我们做三件事:用 AI 时代的工程风险重新解释 SOLID;用 Before/After 对照展示「模型常犯错」与「架构师应如何纠偏」;把 CodeSentinel 的 ReviewPolicy 当作贯穿案例——用策略对象组织不同审核策略,证明 SOLID 不是口号,而是可运行的设计。最后你会拿到一份可粘贴进仓库的 AGENTS.md 规则片段,让 AI 在生成代码时默认走向正确结构。
把话说得更直白一些:AI 并不是故意与你作对,它在最大化「当下可运行」与「最小改动幅度」。而架构师的工作,是在团队里建立一套让「最小改动」与「长期可演进」同向的约束系统。SOLID 恰好提供了五个可教、可检、可门禁的词汇——它们比「写干净点」更容易被工具化,也比「要有架构感」更容易被新人理解。你在 CodeSentinel 里每一次把硬编码分支改成策略注册,都是在降低未来六个月里 LLM 继续往同一个函数里塞逻辑的概率。
本讲还会反复提醒一个事实:开闭原则不是「永远不改旧代码」,而是「把易变点推离稳定核心」。在审核领域,易变点通常是策略集合、规则版本、LLM 提示模板与组织政策;稳定核心通常是审核生命周期、聚合不变式、证据链模型与跨上下文集成契约。抓住这对矛盾,你在评审 AI 生成代码时会非常快:一眼就能看出改动落在错误层级。把「易变/稳定」画成表格贴在评审模板里,团队对齐成本会显著下降。
全局视角:SOLID 与 CodeSentinel 治理链路的映射
CodeSentinel 采用 Clean Architecture + DDD,边界上下文包含 Review、Rule、Repository、Report。SOLID 主要落在 ReviewContext 的应用层与领域层契约上:审核策略如何扩展、规则评估如何替换、报告如何订阅事实而不反向依赖实现。
flowchart TB
subgraph Violation["AI 常见坏味道(统计偏好)"]
V1[上帝类 ReviewService]
V2[巨型 if/else 策略分支]
V3[继承层次随意覆写]
V4[胖接口 AllInOnePort]
V5[内层 import 具体 SDK]
end
subgraph Solid["SOLID 纠偏方向"]
S1[拆分职责:Policy / Runner / Port]
O1[扩展:新增策略类而非改核心]
L1[契约:子类型可替换且不变式成立]
I1[接口隔离:小而稳定的 Protocol]
D1[依赖倒置:内层只认抽象]
end
Violation --> Solid
第二张图给出 ReviewPolicy 策略族 在类结构上的「正确形状」:核心只依赖抽象,具体策略可插拔,契合开闭原则。
classDiagram
class ReviewPolicy {
<<Protocol>>
+name() str
+evaluate(ctx: ReviewContextDTO) PolicyResult
}
class SecurityReviewPolicy {
+name() str
+evaluate(ctx) PolicyResult
}
class ArchitectureReviewPolicy {
+name() str
+evaluate(ctx) PolicyResult
}
class PerformanceReviewPolicy {
+name() str
+evaluate(ctx) PolicyResult
}
class ReviewPolicyRegistry {
-_policies: list~ReviewPolicy~
+register(p: ReviewPolicy)
+all() list~ReviewPolicy~
}
class ReviewOrchestrator {
-_registry: ReviewPolicyRegistry
+run_all(ctx) list~PolicyResult~
}
ReviewPolicy <|.. SecurityReviewPolicy
ReviewPolicy <|.. ArchitectureReviewPolicy
ReviewPolicy <|.. PerformanceReviewPolicy
ReviewPolicyRegistry o-- ReviewPolicy : aggregates
ReviewOrchestrator --> ReviewPolicyRegistry
第三张图用 依赖方向 强调 DIP:应用服务依赖端口,LLM/HTTP/文件系统实现落在外层。
flowchart TB
subgraph Inner["内层:领域 + 应用"]
UC[ReviewOrchestrator / UseCase]
P1[LLMPort Protocol]
P2[RuleEvaluatorPort Protocol]
end
subgraph Outer["外层:基础设施"]
LC[LangChainLLMAdapter]
RE[SemgrepRuleAdapter]
end
UC --> P1
UC --> P2
LC -.实现.-> P1
RE -.实现.-> P2
核心原理:五条原则在 AI 时代的「新解读」
S — 单一职责:AI 为什么爱写「上帝类」?
模型在单次对话里「看到」的是你的即时需求:创建审核、调用规则、写数据库、发通知。它的最优解往往是一个类全包,因为那样最少跨文件跳转。工程上这会导致:变更原因爆炸——任何一条需求抖动都会触动同一坨代码,评审冲突率上升,测试组合爆炸。
AI 时代新解读:单一职责不是「一个类只做一件事」的机械切割,而是把“业务决策点”与“技术细节”分离,把「编排」与「执行」分离。对 CodeSentinel 而言,ReviewOrchestrator 只负责按策略集合驱动流程;具体安全规则扫描、架构层违规检测、性能热点分析应落在不同策略或不同适配器。
O — 开闭:为什么 AI 总用硬编码条件?
开闭原则要求:对扩展开放,对修改关闭。模型却偏爱 if policy == "security": ...,因为显式分支对人类读者最直观。代价是:每加一种策略都要改核心函数,违反 OCP,也让合并冲突成为日常。
AI 时代新解读:你要把「策略选择」从代码分支迁移到注册表 + 多态。新增策略时只新增类并在 Composition Root 注册,核心循环保持稳定。对审核平台这几乎是刚需:企业客户的策略组合千差万别,你不能每次定制都改主干。
L — 里氏替换:继承是模型的「舒适区」,却是系统的雷区
AI 生成继承体系很快,但常常破坏可替换性:子类强化前置条件、弱化后置条件、在不该抛异常的地方抛异常,或在覆写中悄悄引入副作用。对 CodeSentinel 来说,任何 ReviewPolicy 子类若偷偷访问全局单例或写数据库,就会让「替换策略」失去意义。
AI 时代新解读:优先 组合优于继承;若用继承,必须显式写出契约测试(contract tests)保证子类行为一致。对 Python,可用 Protocol 结构化子typing,让静态检查与运行时行为对齐。
I — 接口隔离:胖接口是「一次性满足你所有 import」的陷阱
模型常见写法是定义一个 MegaPort,里面同时包含 scan_repo、call_llm、send_slack、charge_billing……调用方只用到两个方法,却被迫依赖整个协议,测试也必须 stub 一堆方法。
AI 时代新解读:把端口按变更频率与调用方拆分:LLMCompletionPort、NotificationPort、MetricsPort 各自独立。这样 AI 生成代码时也不容易「顺手调用」不该调用的能力。
D — 依赖倒置:具体 import 出现在内层,是架构溃败的起点
AI 极易写出 from openai import OpenAI 出现在 service 文件顶部。短期能跑,长期让内层绑定供应商,切换模型、做离线测试、做合规审计都痛苦。
AI 时代新解读:内层只认识 Protocol/ABC,外层提供适配器。Composition Root 组装具体实现。对 FastAPI,路由可以薄,真正的用例在 application 包,依赖通过构造函数注入。
把五条原则合成一句工程格言
让“变化频繁的部分”可插拔,让“稳定的部分”不被迫反复修改——这就是你与 AI 协作时的护城河。接下来用代码把话说死。
代码实战:Before/After 与 CodeSentinel ReviewPolicy 完整示例
下面示例可在单文件中运行(Python 3.10+),演示 AI 风格坏味道 与 SOLID 重构 对照,并给出 ReviewPolicy 策略注册表。
Before:典型的「AI 一站式」实现(违反 S/O/D)
# bad_ai_style_review.py
# 刻意模拟:上帝类 + 巨型分支 + 具体依赖混在内层
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
@dataclass
class ReviewJob:
repo: str
pr_number: int
policy_name: str
class ReviewService: # 上帝类:什么都能干
def run(self, job: ReviewJob) -> dict[str, Any]:
# 具体“供应商”写死在内层(违反 DIP)
api_key = "sk-demo-not-real"
result: dict[str, Any] = {"findings": []}
# 巨型条件链(违反 OCP)
if job.policy_name == "security":
result["findings"].append({"level": "high", "msg": "疑似密钥硬编码"})
_ = api_key # 假装调用外部扫描
elif job.policy_name == "architecture":
result["findings"].append({"level": "med", "msg": "检测到跨层依赖风险"})
elif job.policy_name == "performance":
result["findings"].append({"level": "low", "msg": "建议为热点路径加缓存"})
else:
result["findings"].append({"level": "info", "msg": f"未知策略 {job.policy_name}"})
# 顺手把通知也写了(职责污染,违反 SRP)
self._notify_slack(f"review done for {job.repo}#{job.pr_number}")
return result
def _notify_slack(self, text: str) -> None:
print(f"[slack] {text}")
if __name__ == "__main__":
svc = ReviewService()
print(svc.run(ReviewJob("codesentinel/core", 42, "security")))
问题清单(评审话术可直接用):
ReviewService同时承担策略选择、(伪)扫描、通知,SRP 崩塌。- 新增策略必须改
run,OCP 崩塌。 api_key与 Slack 通知是具体技术细节,DIP 崩塌。- 难以对单策略做单测,必须打整个服务。
After:策略 + 注册表 + 依赖注入(贴合 CodeSentinel)
# solid_review_policy.py
# runnable: python solid_review_policy.py
from __future__ import annotations
from dataclasses import dataclass
from typing import Protocol, runtime_checkable, Iterable
@dataclass(frozen=True)
class ReviewContextDTO:
repo: str
pr_number: int
@dataclass(frozen=True)
class Finding:
level: str
code: str
message: str
@dataclass(frozen=True)
class PolicyResult:
policy_name: str
findings: tuple[Finding, ...]
@runtime_checkable
class ReviewPolicy(Protocol):
@property
def name(self) -> str: ...
def evaluate(self, ctx: ReviewContextDTO) -> PolicyResult: ...
class SecurityReviewPolicy:
@property
def name(self) -> str:
return "security"
def evaluate(self, ctx: ReviewContextDTO) -> PolicyResult:
# 真实系统里这里调用静态扫描端口;教学示例聚焦结构
return PolicyResult(
policy_name=self.name,
findings=(
Finding("high", "SEC-001", f"{ctx.repo}#{ctx.pr_number}: 检查密钥与敏感信息"),
),
)
class ArchitectureReviewPolicy:
@property
def name(self) -> str:
return "architecture"
def evaluate(self, ctx: ReviewContextDTO) -> PolicyResult:
return PolicyResult(
policy_name=self.name,
findings=(Finding("med", "ARC-010", "边界上下文依赖方向是否违背 Clean Architecture"),),
)
class PerformanceReviewPolicy:
@property
def name(self) -> str:
return "performance"
def evaluate(self, ctx: ReviewContextDTO) -> PolicyResult:
return PolicyResult(
policy_name=self.name,
findings=(Finding("low", "PERF-020", "热点路径是否缺少批量化与缓存策略"),),
)
class ReviewPolicyRegistry:
def __init__(self) -> None:
self._policies: list[ReviewPolicy] = []
def register(self, policy: ReviewPolicy) -> None:
self._policies.append(policy)
def all(self) -> tuple[ReviewPolicy, ...]:
return tuple(self._policies)
@runtime_checkable
class NotifierPort(Protocol):
def send(self, text: str) -> None: ...
class StdoutNotifier:
def send(self, text: str) -> None:
print(f"[notify] {text}")
class ReviewOrchestrator:
"""应用服务:编排策略,依赖抽象(DIP)。"""
def __init__(self, registry: ReviewPolicyRegistry, notifier: NotifierPort) -> None:
self._registry = registry
self._notifier = notifier
def run_policies(self, ctx: ReviewContextDTO, policies: Iterable[str] | None = None) -> list[PolicyResult]:
wanted = set(policies) if policies is not None else None
results: list[PolicyResult] = []
for p in self._registry.all():
if wanted is not None and p.name not in wanted:
continue
results.append(p.evaluate(ctx))
self._notifier.send(f"review finished for {ctx.repo}#{ctx.pr_number}, policies={len(results)}")
return results
def build_default_registry() -> ReviewPolicyRegistry:
reg = ReviewPolicyRegistry()
reg.register(SecurityReviewPolicy())
reg.register(ArchitectureReviewPolicy())
reg.register(PerformanceReviewPolicy())
return reg
if __name__ == "__main__":
orchestrator = ReviewOrchestrator(build_default_registry(), StdoutNotifier())
ctx = ReviewContextDTO("codesentinel/review-context", 108)
for r in orchestrator.run_policies(ctx, policies=("security", "architecture")):
print(r.policy_name, r.findings)
对照说明:
- SRP:
ReviewOrchestrator只做编排;策略类各自评估;通知由NotifierPort承担。 - OCP:新增
ComplianceReviewPolicy只需新建类并register,不改run_policies。 - LSP:所有策略满足同一
ReviewPolicy契约,可互相替换(注意保持无副作用)。 - ISP:
NotifierPort独立于评估逻辑,调用方不被迫依赖巨型接口。 - DIP:编排依赖
Protocol,具体StdoutNotifier可换为 Slack/Webhook 适配器。
里氏替换:一个「坏实现」反例(评审时用来抓 AI)
下面片段不要独立运行(需与上文 ReviewContextDTO 等同处一模块);它展示「同名策略」如何在特殊输入下抛出非预期异常,从而破坏调用方的统一处理逻辑。
class BrokenSecurityPolicy:
"""假装是安全策略,但抛出意外异常,破坏可替换性。"""
@property
def name(self) -> str:
return "security"
def evaluate(self, ctx: ReviewContextDTO) -> PolicyResult:
if "legacy" in ctx.repo:
raise RuntimeError("legacy 仓库直接失败") # 调用方无法统一处理
return PolicyResult(policy_name=self.name, findings=())
治理建议:为 ReviewPolicy 增加 契约测试:所有实现必须在相同输入集上返回 PolicyResult,禁止除「领域定义的业务异常」外的裸异常泄漏;若必须失败,应返回带 Finding 的结构化结果或抛出受控的领域异常类型,而不是裸 RuntimeError。
再谈接口隔离:把「扫描端口」拆成三条稳定边界
在 CodeSentinel 的演进里,最常见的胖接口长这样:ScannerPort 同时包含 scan_secrets、scan_deps、scan_license、scan_binary。结果是:安全策略只想查密钥,却被迫依赖整条工具链;CI 时间上升;本地开发还要 stub 一堆方法。
更合理的 ISP 切分是:
SecretScannerPort:只关心敏感信息模式;DependencyGraphPort:只关心依赖与版本解析;StaticAnalysisPort:只关心通用规则引擎输入输出。
这样 AI 生成「性能策略」时,不容易误调用密钥扫描(因为类型系统层面就不可用),评审者也能用 import 规则强制分层。
再谈依赖倒置:Composition Root 在 FastAPI 里放哪里?
实践上推荐 main.py 或 container.py 只做装配:创建 ReviewPolicyRegistry,注册策略实例,把 ReviewOrchestrator 注入路由依赖。路由函数保持薄:解析 DTO、调用用例、映射 HTTP 状态码。任何 Depends 返回的具体类都应是外层适配器,而不是领域服务里 new 出来的具体实现。
这套边界对 AI 特别关键:模型如果习惯「一个文件搞定」,你要用脚手架约束它:application/ 不允许出现 FastAPI 的 Request 对象;api/ 不允许出现业务规则判断。把约束写进 AGENTS.md 与 linter,比事后 CR 成本低一个数量级。
与 DDD 协同:SOLID 不是替代聚合设计,而是保护聚合不被污染
在 ReviewContext 内,ReviewRequest 聚合负责状态与不变式;ReviewPolicy 更像是领域服务或策略对象(取决于你是否把它建模为无状态服务)。注意:不要让聚合根直接依赖外层端口,否则你会在领域层看到 Protocol 满天飞,聚合变得难以测试。常见做法是:应用服务从仓储取出聚合,调用策略对象得到 PolicyResult,再把结果以领域命令写回聚合或记录为领域事件——SOLID 与 DDD 的分工是:聚合守护业务真相,策略提供可插拔判断,应用服务负责正确顺序。
评审清单(打印贴显示器版)
当你审核 AI 生成的 CodeSentinel 模块时,按下列问题逐条过:
- 这个类是否同时修改了「流程状态」与「外部系统副作用」?
- 新增一种策略是否需要修改
if/elif或match的主干? - 子类是否在父类未声明的情况下改变异常类型或返回值语义?
- 是否存在调用方只用到 1~2 个方法却要依赖 10+ 方法的接口?
domain包是否出现第三方 SDK 的 import?
只要任意一条为「是」,就把重构任务记为 P0,否则技术债会在 LLM 加速下指数膨胀。
生产环境实战:把 SOLID 写进 AGENTS.md,让 AI「默认不跑偏」
在真实团队里,单靠人类评审拦不住 AI 的统计偏好,你需要把架构规则变成模型可见的硬约束。下面是一段可直接放入 CodeSentinel 仓库 AGENTS.md 的片段(你可按团队语气微调):
## 架构生成约束(SOLID / Clean Architecture)
1. 禁止在 `domain/` 与 `application/` 引入具体供应商 SDK(OpenAI、Slack、数据库驱动)。
- LLM、消息、存储必须通过 `ports/` 下的 Protocol 注入。
2. 禁止在应用服务中使用长链 `if/elif` 选择审核策略。
- 必须使用可注册的 `ReviewPolicy` 实现 + `ReviewPolicyRegistry`。
3. 新增能力优先使用组合与新类型,避免 3 层以上继承;若使用继承,必须附带契约测试说明。
4. 接口拆分原则:不要把扫描、通知、计费混在同一 Port;按变更频率拆分为独立 Protocol。
5. 任何跨上下文调用必须通过 ACL 或应用服务编排,不允许领域对象直接依赖外部上下文的具体类。
落地建议:
- 把
AGENTS.md与 CI 静态规则(如 import-linter)联动:内层 import 外层直接失败。 - 在 CodeSentinel 的 ReviewPolicy JSON/YAML 配置 中保存策略启用列表,编排层只读配置,不编译策略名进代码,进一步巩固 OCP。
- 对 AI 生成 PR 启用 结构化评审模板:必须勾选「是否引入新的具体依赖到内层」。
与 CodeSentinel 门禁联动:把 SOLID 变成可度量指标
在生产环境,建议把 SOLID 相关风险映射成可观察信号,而不是停留在口头原则:
- OCP 指标:统计核心编排文件(如
orchestrator.py)在过去 30 天的变更次数与新增分支数;异常升高通常意味着扩展点设计失败。 - DIP 指标:对
application/与domain/运行 import 规则扫描,统计违规 import 数量;把它作为合并门槛。 - SRP 指标:类级别的
git blame熵增(多人频繁修改同一类)可作为上帝类预警。
多团队协作下的「策略配置」与「策略代码」边界
大型企业里,审核策略往往一部分来自安全部门的配置(规则包版本、阈值),一部分来自平台团队的代码(对接内部制品库)。此时要清晰划分:
- 配置层:启用哪些策略、阈值、白名单;
- 代码层:策略如何解释 diff、如何调用端口。
如果把配置硬编码进 Python,AI 会不断生成「临时开关」;如果把复杂逻辑塞进 YAML,人类又难以维护。正确折中是:YAML 只描述策略参数,策略实现仍是类 + 注册表,符合 OCP。
故障复盘:当 AI 生成代码绕过端口直连 SDK
线上典型事故模式是:某位同学习惯在 Jupyter 里验证 OpenAI 调用,随后让模型把同样代码粘贴进服务层,绕过了你们的限流、审计与密钥轮换。DIP 的价值在事故后才会被高估:把供应商调用锁在适配器,你可以在适配器统一加日志、重试、预算控制与合规脱敏。没有这一层,治理平台会沦为「另一个随意调用大模型的脚本集合」。
安全与合规视角:为什么「开闭」与「审计」是同一件事
审核系统天然需要解释「为什么当时给出这条结论」。如果每次扩展都修改核心文件,你会很难把策略版本与代码版本对齐到一次审核结果上。策略注册表 + 独立策略类让你可以把 policy_name、版本号、配置哈希写进报告证据链——这也是架构师在 AI 时代必须强调的:可扩展不仅是开发效率,更是合规与可追溯。
本讲小结
mindmap
root((SOLID x AI))
S单一职责
拆分编排与执行
拒绝上帝类
O开闭原则
注册表多态
拒绝巨型分支
L里氏替换
组合优先
契约测试
I接口隔离
小端口
降低耦合面
D依赖倒置
Protocol在内
适配器在外
治理
AGENTS.md
import门禁
CodeSentinel ReviewPolicy
延伸阅读:把 SOLID 映射到 CodeSentinel 的四个限界上下文
当你把视角从「单个服务类」拉到 Review / Rule / Repository / Report 四个上下文时,SOLID 的落点会更清晰:
- ReviewContext:最需要 OCP 与 SRP。审核主流程稳定,策略与阶段化校验多变;编排类应薄,策略与责任链应厚。
- RuleContext:最需要 ISP 与 DIP。规则引擎可能是 Semgrep、自研 DSL、或未来接 LLM;端口应小、替换成本应低。
- RepositoryContext:最需要 DIP。Git/代码索引/二进制存储差异极大,领域层不能知道你是 GitHub 还是 GitLab。
- ReportContext:最需要 SRP 与事件驱动边界。报告生成与通知发送不要与审核评估搅在一起,订阅事实而不是回调地狱。
这张映射表的价值在于:你能用同一套语言向业务经理解释为什么要拆服务、拆包、拆端口——不是为了炫技,而是为了让 AI 生成功能时「没有捷径可走」,只能走扩展点。
与 Code Review 文化的耦合:SOLID 是评论模板,不是吵架武器
在团队评审里,直接说「你违反 SOLID」往往引发防御。更有效的方式是把原则翻译成可执行改动:
- 「这段逻辑能否抽成
ReviewPolicy实现并在注册表挂载?」 - 「能否把 OpenAI SDK 移到
infrastructure/llm_adapter.py并通过端口注入?」 - 「这个接口能否拆成两个 Protocol,避免调用方依赖不相关方法?」
当评论可执行,AI 辅助修复也会更稳定:你把评论粘贴回模型,它能产生结构正确的补丁。
落地节奏:从「先能跑」到「能演进」的两阶段策略
很多团队会争论:一上来就 SOLID 是否过度设计?对 CodeSentinel 这种明确要长期演进的平台,建议是 两阶段:
- 第一阶段(探索期):允许在
api层快速验证价值,但必须明确哪些文件是「临时代码」,并在 issue 里登记偿还计划。 - 第二阶段(产品化):把稳定用例下沉到
application/domain,引入端口与策略注册表,把第一阶段的分支逻辑迁移为策略对象。
关键在于:探索期不能无限续期。你可以接受短期丑陋,但必须给丑陋设定到期日;否则 LLM 会在丑陋之上继续堆叠,形成不可逆的耦合块。架构师的职责之一,就是把到期日写成里程碑,并在 CI 上加上对应的门禁开关。
小结补遗:SOLID 与性能、成本并不天然对立
有人会把原则与性能对立起来,认为多态与注入会增加调用开销。对 CodeSentinel 这类 IO 密集(Git 拉取、规则扫描、LLM 调用)的系统,真正的成本几乎总在外部网络与模型推理,而不是 Python 多态的分发。相反,良好的 DIP 让你能对昂贵资源做统一缓存、批量请求与熔断,从整体上降低成本。架构师在讨论性能时,别被微优化带偏:先治理依赖方向,再谈热点优化。把这句话写进团队的性能评审 checklist,能避免很多无意义的争论。
思考题
- 你的项目中最近一次「只为加一个小功能却改了核心类」发生在什么场景?如果用
ReviewPolicy模式,边界应画在哪里? - 当 LLM 需要作为「策略的一部分」时,如何避免 DIP 被破坏?端口方法应如何设计才能便于替换供应商?
- 你会为
ReviewPolicy写哪些契约测试,用例数据从哪里来(真实 PR diff 片段还是合成)?
下一讲预告
下一讲进入 设计模式实战:我们会把本讲的策略思想升级为完整的 策略模式、观察者模式、责任链模式 三角组合,展示 CodeSentinel 中 EventBus 如何订阅 ReviewCompleted,以及 ReviewPipeline 如何把多阶段校验拆成可插拔链条——让你既能把 AI 生成代码「接进来」,也能在不变更核心编排的前提下扩展到企业级流程。建议你先复习本讲注册表与端口注入,再看下一讲会更顺滑一些。