地位:

最基础的设计原则

知道如何建立一个稳定的、灵活的系统

定义:

Software entities like classes, modules, and functions should be open for extension bul closed for modifications. - 一个软件实体如类、模块、函数应该对扩展开放,对修改关闭。

What - 什么是开闭原则

一个软件实体应该通过扩展实现变化,而不是通过已有的代码来实现变化。

为软件实体的未来事件而制定的对现象开发设计进行约束的一个原则

软件实体:

  • 项目或软件产品中按照一定的逻辑规则划分的模块
  • 抽象和类
  • 方法

变化:

一个软件产品只要在生命期内,都会发生变化,变化是一个既定的事实

在设计时尽量适应这些变化,以提高项目的稳定性和灵活性,从而实现「拥抱变化」

如果应对一个需求的变化

对于已经投产的项目,当需求发生变化时,有三种应对方式:

  • 修改接口 - 在接口中新增需求需要的方法
    • 操作:所有实现类上都要修改,高层模块调用地方需要改
    • 劣势:作为接口,应该是稳定且可靠的,不应该经常发生变化
  • 修改实现类
    • 操作:直接在相关实现中调整返回需要的内容
    • 劣势:不够灵活
  • 通过扩展实现变化
    • 操作:通过增加子类,覆写方法,高层次的模块通过子类产生新的对象,完成业务变化对系统的最小化开发
    • 优势:修改少,风险小
    • 劣势:需要增加大量的前置思考,以及以前的代码支持对扩展开放
    • 结果:在业务规则改变的情况下,高层模块必须有部分改变以适应新业务,改变要尽量地少,防止变化风险的扩散

三种变化类型:

  • 逻辑变化
    • 只变化一个逻辑,不涉及其他模块
    • 可以通过修改原有类中的方法的方式来完成,前提是所有依赖或关联类都按照相同的逻辑处理
    • 通过修改实现类解决
  • 子模块变化
    • 一个模块变化,会对其他的模块产生影响 - 低层次模块的变化必然引起高层次木块的变化
    • 高层次模块修改时必然的
    • 尽量通过扩展实现变化
  • 可见视图变化
    • 是提供给客户使用的界面,该部分的变化一般会引起连锁反应
    • 尽量通过扩展实现变化,但可能需要重新定义接口

Why - 为什么要使用开闭原则

核心重要性:

开闭原则是最基础的原则,其他五个原则是开闭原则的具体形态。

其他五个原则是指导设计的工具和方法,开闭原则是其精修领袖。

开闭原则是抽象类,其他五大原则是具体的实现类。

优势:

  • 开闭原则对测试的影响

    • 背景:原已投产的代码都是有意义的,经过了严格的测试,保证逻辑正确;包含正常的业务逻辑测试,边界条件测试,异常测试等至少三方面
    • 过程:修改一个或者多个方法代码来完成需求调整,会导致所有测试方法都需要重构
    • 结果:在复杂的断言中进行大量的修改,难免会出现遗漏情况
    • 结论:通过闭合原则,通过新增加类,新增加测试方法的方式,只要保证新增的类正确就可以了
  • 开闭原则可以提高复用性

    • 建议:在面向对象的设计中,所有的逻辑都是从原子逻辑组合而言,而不是在一个类中独立实现的业务逻辑。力度越小,被复用的可能性就越大
    • 为什么复用:可以减少代码量,避免相同的逻辑分散在多个角落,避免为了修改一个微小的缺陷或增加新功能而要在整个项目中到处查找相关的代码
    • 如何提高复用率:缩小逻辑粒度,知道一个逻辑不可再拆分位置
  • 开闭原则可以提高可维护性

    • 结果:当需要对程序进行扩展时,扩展一个类会比修改一个类更舒服
  • 面向对象开发的要求

    • 建议:在设计之初考虑到所有可能变化的因素,然后留下接口

How - 怎么使用开闭原则

开闭原则是一个✨非常虚✨的原则

怎么应用:

  • 抽象约束

    • 抽象是对一组事物的通用描述,没有具体的实现,也就代表它可以有非常多的可能性,可以随需求的变化而变化
    • 通过接口或抽象类可以约束一组可能变化的行为,并且能够实现对外扩展开放
      • 通过接口或抽象类约束扩展,对扩展进行边界限定:如不允许出现在接口或抽象类中不存在的public方法
      • 参数类型、引用对象尽量使用接口或者抽象类,而不是实现类
      • 抽象层尽量保持稳定,一旦确定即不允许修改;接口或抽象类一旦定义,就应该立即执行,不能有修改接口的思想,除非是彻底的大返工。
    • 要实现对扩展开饭,首要前提是抽象约束
  • 元数据(metadata)控制模块行为

    • 尽量使用元数据来控制程序的行为,减少重复开发 -- 配置参数
    • 通过扩展一个子类,修改配置文件,完成了业务变化
  • 制定项目章程

    • 指定所有人员必须遵守的约定,对于项目来说,约定犹豫配置
    • 需要一个团队有较高的自觉性,需要一个较长时间的磨合,一旦项目成员都熟悉这样的规则,比铜鼓哦接口或抽象类进行约束效率更高,而且扩展性没有减少
  • 封装变化

    • 将相同的变化封装到一个接口或抽象类中
    • 将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中

最佳实践

结合六大设计原则,来应对需求的变化

  • Single Responsibility Principle: 单一职责原则
  • Open Closed Principle: 开闭原则
  • Liskov Substitution Principle: 里氏替换原则
  • Law of Demeter: 迪米特法则
  • Interface Segregation Principle: 接口隔离原则
  • Depencence Inversion Principle: 依赖倒置原则

组合起来是变成 SOLID(稳定的)。

核心:为了建立稳定、灵活、简装的设计。

注意事项:

  • 开闭原则也只是一个原则

    • 尽量采用,灵活使用
  • 项目规章非常重要

    • 章程是一个团队所有成员共同的知识结晶,是所有成员必须遵守的约定
    • 提高开发效率、降低缺陷率、提高团队士气、提高技术成员水平
  • 预知变化

    • 架构师设计一套系统不仅要符合现有的需求,还要适应可能发生的变化,这擦拭一个优良的架构
    • 无法百分百预知变化,但是可以朝着这个方向前进,显著地盖上一个系统的架构

读《设计模式之禅》第六章 开闭原则