系列文章|源码
https://github.com/tyronczt/design-mode-learn
概述
软件项目中,需求是不断变化的,需求也是项目中最难把控的,需求的变更也是无法避免的。我们写的软件程序,如何能实现拥抱变化,使我们的软件达到 可维护 和 可复用 ,这是一代代软件工程师不断追寻的真理。
导致一个软件的可维护性较低的原因有四个:
- 过于僵硬(Rigidity)
很难在一个软件系统里加入新的特性,一旦加入新的特性就会影响到其他模块的功能,最后会变成跨越几个模块的改动,由于设计上的缺陷,导致不敢轻易往原有的系统中添加新的特性的僵硬化的情况。
- 过于脆弱(Fragility)
与过于僵硬同时存在。对一个地方的修改,往往会导致看起来没有什么关系的另一个地方发生故障,尽管在修改前,设计师会竭尽所能预测可能的故障点,但是在修改完成之前,系统的原始设计师甚至都无法预测到可能会波及的地方,这种一碰就碎的情况,就是软件系统过于脆弱。
- 复用率低(Immobility)
所谓复用,就是指一个软件的组成部分,可以在一个项目的不同地方甚至不同的项目中重复使用。每当程序员发现一段代码、函数、模块的功能可以在新模块或者新系统中使用,但是发现这些现存的代码、函数、模块依赖于一大堆其他的东西,以至于很难将他们分开。最后,他们发现最好的办法就是不去碰这些东西,而是重写自己的代码。
这样的系统存在复用率低的问题。
- 黏度过高(Viscosity)
对系统的改动可以采取保存原始设计意图和设计框架的方式,也可以以破坏原始意图和框架的方式进行。前者对系统未来有利,而后者是权宜之计,可以解决短期问题,但会牺牲中长期利益。如果一个系统设计,总是使得第二种办法比第一种容易,就叫黏度过高。
一个好的系统设计应该具备如下性质:
- 可扩展性(Extensibility)
新的特性很容易加入到系统中去,就是可扩展性,就是“过于僵硬”反面。
- 灵活性(Flexibility)
可以允许代码修改平稳地发生,而不会波及到很多其他的模块。灵活性就是“过于脆弱”的反面。
- 可插入性(Pluggability)
可以很容易的将一个类抽出去,同时将一个有同样接口的类加入进来,这就是可插入性。
提高系统可维护性和可复用性的设计原则
面向对象设计原则为支持可维护性复用而诞生,这些原则蕴含在很多设计模式中,它们是从许多设计方案中总结出的指导性原则。面向对象设计原则也是我们用于评价一个设计模式的使用效果的重要指标之一。
单一职责原则(Single Responsibility Principle, SRP)
一个类只负责一个功能领域中的相应职责
开闭原则(Open-Closed Principle, OCP)
软件实体应对扩展开放,而对修改关闭
里氏代换原则(Liskov Substitution Principle, LSP)
所有引用基类对象的地方能够透明地使用其子类的对象
依赖倒转原则(Dependence Inversion Principle, DIP)
抽象不应该依赖于细节,细节应该依赖于抽象
接口隔离原则(Interface Segregation Principle, ISP)
使用多个专门的接口,而不使用单一的总接口
合成复用原则(Composite Reuse Principle, CRP)
尽量使用对象组合,而不是继承来达到复用的目的
迪米特法则(Law of Demeter, LoD)
一个软件实体应当尽可能少地与其他实体发生相互作用
软件的可复用性
复用的重要性
较高的生产率,较高的软件质量,恰当的复用可以改善系统的可维护性。
传统的复用
代码的剪贴复用,算法的复用,数据结构的复用。
面向对象的设计的复用
面向对象的语言中,数据的抽象化、继承、封装和多态的特性是几项最重要的语言特性,这些特性使得一个系统可以在更高的层次上提供可复用性。数据的抽象化和继承可以使得概念和定义得以复用,多态性使得实现和应用可以复用,而抽象化和封装可以保持和促进系统的可维护性。这样,复用的焦点不再集中在函数、算法等具体的实现细节上,而是集中在最重要的具有宏观商业逻辑的抽象层次上。
抽象层是一个应用系统做战略性判断和决定的地方,那么抽象层次应当是较为稳定的,应当是复用的重点。如果抽象层次的模块相对独立于具体层次的模块,那么具体层次的变化不会影响到抽象层次的,所以抽象层次模块的复用会比较容易。
在面向对象的设计里,可维护性复用是以设计原则和设计模式为基础的。
对可维护性的支持
首先,适当地提高系统的可复用性,可以提高系统的可扩展性。允许一个具有同样接口新类代替旧的类,是对抽象接口的复用。客户端依赖于一个抽象的接口,而不是一个具体的实现类,使得这个具体的类可以被其他的类取代,而不需要修改客户端的代码。
系统的可扩展性是由开闭原则、里氏代换原则、依赖倒转原则和合成复用原则实现的。
其次,适当地提高系统的可复用性,可以提高系统的灵活性。在一个设计得当的系统中,每一个模块都相对于其他模块独立存在,并且保持与其他模块尽可能少的通信。这样一来,在其中一个模块的代码发生修改的时候,这个修改的压力不会传递到其他模块。
系统的灵活性是有开闭原则、迪米特法则、接口隔离原则保证的。
最后,适当地提高系统的可复用性,可以提高系统的可插入性,在一个符合“开-闭“原则的系统中,抽象层封装了与商业逻辑有关的重要行为,这些行为的具体实现由实现层给出。当一个实现类再满足需要,需要以另一个实现类取代的时候,系统的设计就可以保证旧的类可被拔出,新类可以被插入。
可插入性有开闭原则、里氏代换原则、合成复用原则、依赖倒转原则保证。