# O 开闭原则

> Software entities (modules, classes, functions, etc.) should be open for extension, but closed for modification.

1. 软件实体（模块、类、方法等）应该“对扩展开放、对修改关闭”
2. 添加一个新的功能，应该是：在已有代码基础上扩展代码（新增模块/类/方法等）而非修改已有代码
3. 开闭原则是为了实现代码的扩展性
   * 在 23 种经典设计模式中，大部分都是为了解决代码的“扩展性”问题而存在的，主要遵从的就是“开闭原则”

理解的难点在于：

1. 怎样的代码改动才被定义为‘扩展’？
2. 怎样的代码改动才被定义为‘修改’？
   * 开闭原则可以应用在不同粒度的代码中（模块、类、方法及其属性）
   * 同样一个代码改动，在粗代码粒度下可能被认定为“修改”，在细代码粒度下又被认定为“扩展”
3. 怎么才算满足或违反‘开闭原则’？
4. 修改代码就一定意味着违反‘开闭原则’吗？
   * 开闭原则并不是说完全杜绝修改，而是以最小的修改代码的代价来完成新功能的开发
   * 添加一个新功能是不可能任何模块、类、方法的代码都不“修改”的，我们要做的是尽量让修改操作更集中、更少、更上层，尽量让最核心、最复杂的那部分逻辑代码满足开闭原则
   * 回到设计初衷：只要它没有破坏原有代码的正常运行，没有破坏原有的单元测试，就是一个合格的代码改动
     * 比如参数变了，会导致调用该接口的代码都要修改（将参数封装成一个类）
     * 比如函数体变了，会导致单元测试要修改

应用的难点在于：

1. 如何做到‘对扩展开放、修改关闭’？
   1. 时刻具备扩展意识、抽象意识、封装意识
      * 这些“潜意识”可能比任何开发技巧都重要
      * 将可变部分封装起来，隔离变化，提供抽象化的不可变接口，给上层系统使用
      * 最常用来提高代码扩展性的方法有：多态、依赖注入、基于接口而非实现编程，以及大部分的设计模式（比如装饰、策略、模板、职责链、状态等）
   2. 关键是预留扩展点。如何才能识别出所有可能的扩展点？
      * 若是业务导向的，就要对业务有足够的了解，能够知道当下以及未来可能要支持的业务需求
      * 若是业务无关的、通用的、偏底层的系统（框架/组件/类库），则需要了解“它们会被如何使用？今后你打算添加哪些功能？使用者未来会有哪些更多的功能需求？”等
      * 设计代码结构，事先留好扩展点，以便在未来需求变更的时候，在不改动代码整体结构、做到最小代码改动的情况下，将新的代码灵活地插入到扩展点上
      * 也可以等有需求驱动的时候，再通过重构代码的方式来支持扩展的需求（没必要为一些遥远的、不一定发生的需求去提前买单，做过度设计）
2. 如何在项目中灵活地应用‘开闭原则’，以避免在追求扩展性的同时影响到代码的可读性？
   * 在有些情况下，代码的扩展性会和可读性相冲突
   * 很多时候，我们都需要在扩展性和可读性之间做权衡

实际上，开闭原则讲的就是代码的扩展性问题，是判断一段代码是否易扩展的“金标准”。如果某段代码在应对未来需求变化的时候，能够做到“对扩展开放、对修改关闭”，那就说明这段代码的扩展性比较好。所以，问如何才能做到“对扩展开放、对修改关闭”，也就粗略地等同于在问：如何才能写出扩展性好的代码。
