开闭原则讲什么
开闭原则由 Bertrand Meyer 提出:软件实体应当对扩展开放、对修改关闭。落到工程实践里,就是在需求增加新类型时尽量只新增代码(新实现、新配置项),而不是反复修改同一条判断链路。它不意味着永不修改——扩展点本身仍会演进,只是把变化集中到少量稳定接口上。
基本信息
| 提出者 | Bertrand Meyer(1988 年) |
|---|---|
| 落地手段 | 策略模式、多态、插件注册、配置驱动 |
| 前置条件 | 先稳定变化点:确认哪些维度会持续增加 |
| 常见误区 | 把开闭原则理解为「代码永远不改」 |
什么时候值得引入扩展点
- 同一段分支判断已经因为新类型改过两次以上。
- 分支内部逻辑较长,且各分支之间几乎没有共享代码。
- 新增类型的时间点可预期(如新支付渠道、新导出格式)。
- 分支的取舍需要由配置或运行时决定,而不是编译期固定。
- 改动这段代码的风险较高,希望把变化限制在新文件里。
扩展点形态
扩展点形态
// 新增支付方式:只新增实现类并注册,不改调用链
interface PaymentMethod { PayResult pay(Order o); }
class Alipay implements PaymentMethod { ... }
class WechatPay implements PaymentMethod { ... }
// 调用侧通过注册表或依赖注入按名称取实现改分支与加扩展点对比
| 维度 | 持续改分支 | 扩展点 |
|---|---|---|
| 新增类型 | 改动集中在同一处,冲突与风险叠加 | 新增实现文件,改动隔离 |
| 可测试性 | 需要构造大量分支条件 | 每个实现可单独测试 |
| 理解成本 | 一个函数里读所有类型 | 需要按接口追踪实现位置 |
| 适用前提 | 类型少且稳定 | 类型会持续增加 |
开闭原则不是「永不修改」:扩展点本身仍会演进,只是把变化集中到少量稳定的接口上。
先确认变化方向再抽象:在只有一种实现的场景里预先设计插件机制,通常只是在增加认知成本。
扩展点要在调用侧集中注册:实现分散在多个文件中查找,会让「新类型」这件事重新变得昂贵。