浅谈软件设计原则

124 阅读5分钟

好久没写文章了,这里整理下以前看过的一些软件设计原则的资料吧,让我们一起看看吧。

开闭原则

概念: Open-Closed Principle, OCP, 一个软件实体如类,模块,函数应该对扩展开放,对修改关闭, 强调用抽象构建框架,用实现扩展细节,提高软件系统的可复用性和可维护性。

理解:

  • 开闭原则可以应用在不同粒度的代码中,可以是模块,可以是类,还可以是方法。 同一个代码改动,在粗粒度上是修改,但在细粒度上是扩展。所以没必要纠结是修改还是扩展,判定代码改动的合格标准应该是没有破坏原来代码的运行,没有破坏原有的单元测试。
  • 在编写代码时,思考代码可能变更点,事先留好扩展点, 封装可变部分,隔绝变化,提供抽象的不可变接口。
  • 开闭原则并不是万能的。我们需要注意代码的扩展性和可读性的平衡。

依赖倒置原则

概念: Dependence Inversion principle, DIP。设计代码结构时,高层模块不应依赖于底层模块, 二者都应该依赖于抽象。抽象不应该依赖细节,细节应该依赖于抽象。通过依赖倒置,减少类与类之间的耦合性,提高系统的稳定性, 提高代码的可读性和可维护性,降低修改程序的风险。

补充:

  • 控制反转(IOC)

    这里说的不是Spring IOC,而是一种设计思想。核心是框架提供了一个可扩展的代码骨架,用来组装对象,管理整个执行流程。在利用框架开发的时候,只需要往预留的扩展点,添加与业务有关的代码,就利用框架来驱动整个程序流程的执行。流程控制权由程序员"反转"到框架。

    控制反转的实现方法很多,比如模板设计模式, 依赖注入等。

    依赖注入框架很多,比如Spring, Google Guice等。

理解:

  • 以Tomcat为例,Tomcat是高层模块,Web应用程序代码是低层模块,Tomcat和Web应用程序没有直接的依赖关系,两者都依赖Servlet规范。

单一职责原则

概念: Simple Responsibility Principle, SRP。不要存在多于一个导致类变更的原因。也就是不要涉及大而全的类,要设计粒度小,功能单一的类。

理解:

  • 如何判断一个类的职责是否单一?
    • 类中的代码行数,函数或属性过多
    • 类依赖的其他类过多
    • 私有方法太多
    • 类名比较难起
    • 大量的方法集中操作类中的某几个属性
  • 单一职责原则不是银弹, 避免拆分的过细,导致降低内聚性,影响可维护性。

接口隔离原则

概念: Interface Segregation Principle, ISP。使用多个专门的接口,客户端不应该依赖它不需要的接口。

理解:

  • 和单一职责原则的区别:
    • 单一职责职责针对的是模块,类,接口的设计
    • 接口隔离原则提供了一种判定接口是否职责单一的标准: 通过调用者如何使用接口来间接判定,如果调用者只使用了部分接口或接口的部分功能,那接口的设计不够职责单一。
  • 接口设计要尽量单一,尽量细粒度。

迪米特法则

概念: Law of Demeter, LOD。又叫最小知道原则(Least Knowledge Priciple, LKP)。尽量降低类与类的耦合,迪米特法则主要强调只和朋友交流,不和陌生人说话,出现在成员变量,方法输入/输出参数中的类可以称为成员朋友类,出现在方法体内部的类不属于朋友类。

理解:

  • 主要强调的是高内聚,松耦合。高内聚指导类的设计,松耦合指导类与类之间依赖关系的设计。
  • 不该有直接依赖关系的类之间不要有依赖。有依赖关系的类之间,尽量只依赖必要的接口。

里氏替换原则

概念: 子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏。约束继承泛滥,加强程序的健壮性, 兼容性,维护性和扩展性。

理解:

  • 子类可以实现父类的抽象方法,不能覆盖父类的非抽象方法。也就是违背父类声明要实现的功能
  • 子类不能违背父类对于输入,输出,异常的约定
  • 里氏替换原则是用来指导,继承关系中子类该如何设计的原则。子类的设计要保证在替换父类时,不改变原有程序的逻辑,不破坏原有程序的正确性。

合成复用原则

概念: Composite/Aggregate Reuse Principle, CARP。尽量使用对象组合/聚合,而不是继承关系达到软件复用的目的,可以使系统更灵活, 降低类与类之间的耦合。

理解:

  • 维持类的封装性,合成复用又称为"黑箱"复用。
  • 合成复用耦合性低
  • 灵活性高,复用可以在运行时动态进行。

其他原则

除了七种经典的软件设计原则,还有KISS,YAGNI,DRY原则。

  • KISS: 尽量保持简单
    • 不过度优化
    • 不重复造轮子
    • 不使用复杂的技术解决简单问题
  • YAGNI: 不做过度设计
    • 不提前编写代码,预留好扩展点
    • 不提前引入不需要的开发包
  • DRY: 不重复代码
    • 实现逻辑重复,但功能语义不重复的代码,并不违反 DRY 原则。实现逻辑不重复,但功能语义重复的代码,也算是违反 DRY 原则。除此之外,代码执行重复也算是违反 DRY 原则
    • 代码复用性方法:
      • 减少代码耦合
      • 满足单一职责原则
      • 模块化业务与非业务逻辑分离
      • 通用代码下沉
      • 继承、多态、抽象、封装应用模板等设计模式