--- name: software-design-patterns description: Use when code structure is under pressure — growing if/switch branches, scattered status checks, tight coupling to third-party SDKs, unclear layering or transaction boundaries, extensibility demands, domain modeling, or cross-service reliability (dual writes, retries, sagas, idempotency). Guides selecting, rejecting, and combining GoF, enterprise, DDD, and distributed-system patterns by evidence, and recommends keeping the code simple when the pressure is unproven. Trigger on 重构、解耦、架构评审、扩展点、状态机、双写一致性、重试熔断、CQRS requests even if the user never says "设计模式". --- # 软件设计模式:选择、拒绝与组合 参考文件是设计决策记录,不是名词解释。按"压力 → 候选 → 排除"推进,不要从模式名反推需求。 ## 核心原则 1. **先识别变化点**:什么在变?变化频率?是否已产生维护成本? 2. **最小复杂度优先**:简单分支、普通函数、组合、语言原生特性够用时不引入模式。 3. **收益必须可验证**:降耦合、隔离变化、维护不变量、明确事务边界、提可靠性或可测性。 4. **成本必须计入**:接口、对象、间接调用、状态、异步、持久化、运维。 5. **不为"未来可能"设计**:只有变化有证据或结构已产生压力时才加抽象。 ## 工作流 1. **先查日志**:若项目已有设计决策日志,读索引,按模块/变化轴定位受影响的既有记录(见「设计决策日志」)。需求变更时从这一步开始,不要重新从零判断。 2. **说清压力**:指出条件分支、耦合、职责、状态、事务、一致性或可靠性上的具体问题。 3. **路由**:用下方「快速路由」表筛出 **最多 3 个候选**。若命中「停止条件」,直接建议保持简单,**不再读任何参考文件**。 4. **验证候选**:打开候选模式文件,只看 `什么时候使用` / `什么时候不要使用` / `常见误用` / `退出条件`。 5. **组合检查**:推荐 2 个及以上模式时,读 [常见模式组合](references/pattern-combinations.md),确认职责不重叠、故障链可解释。 6. **输出**:按「输出模板」给结论。 7. **落日志**:把结论写入设计决策日志。不是可选项——没有记录的决策等于下次需求变更时重新猜。 **只在**路由表未命中、问题跨多个层级、或需要评分/迁移顺序时,才读 [设计模式决策指南](references/decision-guide.md)。 ## 快速路由 | 设计压力 | 首选候选 | 通常不该用的情况 | |---|---|---| | `if/switch(type)` 随实现数量增长 | Strategy + Factory Method | 分支少且稳定 | | `if/switch(status)` 散落 | State | 状态仅是数据标签 | | 固定流程中少数步骤变化 | Template Method | 变化维度多、组合需求强 | | 连续规则/过滤且可短路 | Chain of Responsibility | 步骤强耦合、全部必须执行 | | 一个事实触发多个反应 | Observer / Domain Event | 强一致动作必须同事务完成 | | 第三方/遗留接口不兼容 | Adapter | 接口本来就兼容 | | 横切访问控制、事务、远程替身 | Proxy | 只是动态叠加功能时更像 Decorator | | 功能可按需层层增强 | Decorator | 包装顺序复杂到难以理解 | | 两个正交变化维度导致类爆炸 | Bridge | 只有一个变化维度 | | 创建参数多、构造分阶段 | Builder | 简单构造/命名参数已足够 | | 需要成套切换产品族 | Abstract Factory | 只有一种产品 | | 复杂子系统需要统一入口 | Facade | Facade 只是无价值转发 | | 树形结构统一处理叶子/容器 | Composite | 实际是图或行为差异巨大 | | 领域对象需要稳定身份 | Entity | 只由值决定时用 Value Object | | 领域对象只由值决定 | Value Object | 有独立身份/生命周期 | | 强一致对象组需要事务边界 | Aggregate + Aggregate Root | 只是数据库关联关系 | | 领域规则不自然属于实体 | Domain Service | 只是应用编排或技术服务 | | 聚合持久化需要隔离 ORM | DDD Repository | 简单 CRUD 或报表查询 | | 集合式持久化访问抽象 | Enterprise Repository | 富领域模型应按聚合根建模 | | DB 更新 + MQ 发布双写 | Outbox | 没有跨进程事件传播 | | 网络瞬态失败 | Retry + Timeout | 永久错误、非幂等写 | | 持续故障导致资源耗尽 | Circuit Breaker + Bulkhead | 本地廉价调用 | | 跨服务长事务 | Saga | 单库 ACID 足够 | | 写模型复杂、读模型差异大 | CQRS | 普通 CRUD | | 需要完整事实历史/重放 | Event Sourcing | 只为审计日志 | | 重复请求/消息不能重复副作用 | Idempotency | 天然幂等读操作 | ## 停止条件:保持简单 命中任一条件时推荐"不使用模式": - 只有一个实现,且未来变化无证据。 - 分支少、稳定、可读,抽象后跳转成本高于收益。 - 新模式引入状态/异步/持久化复杂度,但无对应可靠性或一致性需求。 - 团队无法测试、观测或运维该模式(Event Sourcing、Saga、复杂事件流)。 - 语言或框架已有原生能力:命名参数、DI 容器作用域、平台服务发现、K8s Service。 - 根因是职责划分或数据模型错误——先修边界,不要用模式掩盖。 - 为了消除一个很小很稳定的 `if` 引入多个接口和类;把模式数量当质量指标。 ## Gotchas - **同名不同物**:`enterprise/repository.md` 是集合式持久化抽象(适合 CRUD/查询封装),`ddd/repository.md` 是聚合根级仓储(富领域模型、强事务边界)。选错会让 ORM 类型渗进领域层。 - **Strategy vs State**:Strategy 由调用方/注册表选择,策略之间互不感知;State 由状态对象自己触发迁移并约束合法转换。 - **Decorator vs Proxy**:Decorator 动态叠加职责、调用方知道包装存在;Proxy 控制访问(权限、事务、懒加载、远程替身),真实对象对调用方不可见。 - **Outbox ≠ exactly-once**:只保证至少一次投递,消费端必须按业务键幂等。 - **Saga 补偿 ≠ rollback**:补偿是另一个业务动作,本身会失败,需要重试、可观测和人工兜底。 - **CQRS 与 Event Sourcing 不绑定**:可各自独立采用,不要默认成对引入。 - **Singleton 优先用 DI 容器作用域**,不要手写全局静态实例——那是隐藏的全局状态和测试难题。 - **Adapter 不负责业务编排**:适配层只做类型/协议转换,否则会变成第二个服务层。 - **超时先于重试**:多层无预算重试会放大流量,必须有总预算和退避上限。 ## 输出模板 ```markdown ## 压力 <具体代码/架构压力;变化频率、失败后果、当前已产生的成本> ## 结论 <主模式>(必要时:<辅助模式>),或明确写「不引入模式,保持 <更简单的做法>」 记录:(本次是新建 / 追加 / 取代 / 废弃) ## 理由与排除 - 命中信号:<可观察证据,不是"最佳实践"> - 排除 <最相近候选>:<为什么> ## 最小落地 角色 / 接口所在层 / 创建与装配位置 / 一次请求或消息的调用时序 ## 成本与退出条件 <新增间接层、状态、网络跳数、存储、运维负担>;<需求规模下降或假设不成立时如何回退> ## 验证 <不变量测试、边界/并发/重试用例、需要打的指标与日志> ``` 异步模式(事件、Outbox、Saga、CQRS、重试)必须在「验证」里写明重复、乱序和恢复策略。 ## 设计决策日志(可迭代) 设计结论必须沉淀成记录,随需求变更演进。历史只增不改:旧记录保留,靠状态和「变更历史」表达演进。 **位置**:优先沿用项目已有约定(`docs/adr/`、`docs/decisions/`、`ADR/`、已有设计日志);都没有则用 `docs/design-decisions/`,索引为 `INDEX.md`。 **创建记录**: ```bash python3 scripts/new_decision.py "支付渠道分支改用 Strategy" # 自动编号 + 更新索引 python3 scripts/new_decision.py "订单状态机改用 State" --supersedes 0001 ``` 脚本从 [assets/decision-record-template.md](assets/decision-record-template.md) 生成骨架(压力 / 候选与排除 / 结论 / 最小落地 / 成本与退出条件 / 验证 / 变更历史),填内容即可。编号永不复用,不覆盖已有文件。 **需求变更时的三种动作**: | 情况 | 动作 | |---|---| | 结论不变,只是约束/规模变化 | 在原记录「变更历史」追加一行:日期、需求变更、结论是否变 | | 结论变了 | 新建记录并 `--supersedes <原编号>`;原记录状态改为「已被取代」,保留原文 | | 命中原记录的「退出条件」 | 原记录状态改为「已废弃」,新建记录写明回退方案与回退后的更简单设计 | **复盘触发**:每次需求变更后,重扫相关记录的「退出条件」。命中就回退,不要因为已经实现过就继续保留模式。**禁止**删除或静默覆盖旧记录,也禁止只改结论不写理由。 ## 采纳前自检 输出建议前确认: - [ ] 压力是真实的(已有 ≥2 个实现/分支,或已产生回归),不是"未来可能"。 - [ ] 已比较至少一个相近候选并说明排除理由。 - [ ] 更简单方案(函数、参数、组合、平台能力)已被明确排除。 - [ ] 团队能测试和观测新增间接层;异步模式已说明重复/乱序/恢复。 - [ ] 结论已落入设计决策日志:新建记录,或在既有记录上追加变更/取代/废弃。 任一项未通过 → 回到「停止条件」,优先保持简单。 ## 模式索引 ### GoF / 创建型 - [Singleton](references/gof/creational/singleton.md) · [Factory Method](references/gof/creational/factory-method.md) · [Abstract Factory](references/gof/creational/abstract-factory.md) · [Builder](references/gof/creational/builder.md) · [Prototype](references/gof/creational/prototype.md) ### GoF / 结构型 - [Adapter](references/gof/structural/adapter.md) · [Bridge](references/gof/structural/bridge.md) · [Composite](references/gof/structural/composite.md) · [Decorator](references/gof/structural/decorator.md) · [Facade](references/gof/structural/facade.md) · [Flyweight](references/gof/structural/flyweight.md) · [Proxy](references/gof/structural/proxy.md) ### GoF / 行为型 - [Chain of Responsibility](references/gof/behavioral/chain-of-responsibility.md) · [Command](references/gof/behavioral/command.md) · [Interpreter](references/gof/behavioral/interpreter.md) · [Iterator](references/gof/behavioral/iterator.md) · [Mediator](references/gof/behavioral/mediator.md) · [Memento](references/gof/behavioral/memento.md) · [Observer](references/gof/behavioral/observer.md) · [State](references/gof/behavioral/state.md) · [Strategy](references/gof/behavioral/strategy.md) · [Template Method](references/gof/behavioral/template-method.md) · [Visitor](references/gof/behavioral/visitor.md) ### 企业应用模式 - [Repository](references/enterprise/repository.md) · [Service Layer](references/enterprise/service-layer.md) · [Unit of Work](references/enterprise/unit-of-work.md) · [Data Mapper](references/enterprise/data-mapper.md) · [Active Record](references/enterprise/active-record.md) · [DTO](references/enterprise/dto.md) · [Dependency Injection](references/enterprise/dependency-injection.md) · [MVC](references/enterprise/mvc.md) ### DDD 模式 - [Entity](references/ddd/entity.md) · [Value Object](references/ddd/value-object.md) · [Aggregate](references/ddd/aggregate.md) · [Aggregate Root](references/ddd/aggregate-root.md) · [DDD Repository](references/ddd/repository.md) · [Domain Service](references/ddd/domain-service.md) · [Domain Event](references/ddd/domain-event.md) · [Application Service](references/ddd/application-service.md) · [DDD Factory](references/ddd/factory.md) · [Specification](references/ddd/specification.md) ### 分布式系统模式 - [Saga](references/distributed/saga.md) · [CQRS](references/distributed/cqrs.md) · [Event Sourcing](references/distributed/event-sourcing.md) · [Transactional Outbox](references/distributed/outbox.md) · [Circuit Breaker](references/distributed/circuit-breaker.md) · [Retry](references/distributed/retry.md) · [Bulkhead](references/distributed/bulkhead.md) · [API Gateway](references/distributed/api-gateway.md) · [Service Discovery](references/distributed/service-discovery.md) · [Idempotency](references/distributed/idempotency.md) · [Leader Election](references/distributed/leader-election.md)