可维护性的度量与构造原则
本节是宏观介绍:
(1)什么是软件维护;
(2)可维护性如何度量;
(3)实现高可维护性的设计原则
1. 软件维护和演化
1.1 软件维护
- 软件维护:修复错误、改善性能。是一个软件开发迭代周期的最后一个阶段
- 运维工程师:运维是软件开发中最困难的工作之一, 处理来自用户报告的故障/问题。大部分运维工作不需要修改代码,但有的时候也需要修改代码。
- 修复问题之后:一定要做足够的文档记录和测试,不能引入新的bug。
- 维护的种类:
- 纠错性
- 适应性
- 完善性
- 预防性
1.2 软件演化
- 软件演化:对软件进行持续的更新。
- 软件的大部分成本来自于维护阶段。
- “变化”在软件生命周期中是不可避免的,要在最初的设计中充分考虑到未来的变化。
1.3 软件维护的例子
- 模块化:高内聚,低耦合
- OO设计原则:SOLID、GRASP
- OO设计模式
- 基于状态的构造技术
- 表驱动的构造技术
- 基于语法的构造技术
2. 可维护性指标
- Code review的时候经常问的关于可维护性的问题:
- 设计结构是否足够简单?
- 模块之间是否松散耦合?
- 模块内部是否高度聚合?
- 是否使用了非常深的继承树,是否使用了delegation替代继承?
- 代码的圈复杂度是否太高?
- 是否存在重复代码?
- 一些常用的维护指标
- 圈复杂度
- 代码行数
- 可维护性指数
- 继承的层次数
- 类之间的耦合度
- 单元测试的覆盖度
3. 模块设计和模块原则
3.1 评估模块性的五个标准
- 可分解性:将问题分解为各个可独立解决的子问题。目标:使模块之间的依赖关系显式化和最小化
- 可组合型:可容易的将模块组合起来形成新的系统。目标:使模块可在不同的环境下复用
- 可理解性:每个子模块都可被系统设计者容易的理解。
- 可持续性:小的变化将只影响一小部分模块,而不会影响整个体系结构
- 出现异常之后的保护:运行时的不正常将局限于小范围模块内
3.2 模块化设计的五个规则
- 直接映射:模块的结构与现实世界中问题领域的结构保持一致。对持续性和可分解性产生影响。
- 尽可能少的接口:模块应尽可能少的与其他模块通讯。对以下评价标准产生影响:可持续性、保护性、可理解性、可组合性
- 尽可能小的接口:如果两个模块通讯,那么它们应交换尽可能少的信息。对“**可持续性”**和“保护性”产生影响
- 显式接口:当A与B通讯时,应明显的发生在A与B的接口之间。受影响的评价标准:可分解性、可组合性、可持续性、 可理解性
- 信息隐藏:经常可能发生变化的设计决策应尽可能隐藏在抽象接口后面
4. OO设计原则:SOLID
4.1 单一责任原则(SRP)
- 原则:不应该有多于1个原因让你的ADT发生变化,否则就拆分开(让一个类实现一个非常具体的功能)
- 实现本质:一个类,一个责任
- 如果一个类包含了多个责任,那么将引起不良后果,例如:引入额外的包,占据资源,导致频繁的重新配置、部署等。
- 最简单的原则,却是最难做好的原则
4.2 开放封闭原则 (OCP)
- 对扩展性的开放:模块的行为应是可扩展的,从而该模块可表现出新的行为以满足需求的变化
- 对修改的封闭:模块自身的代码是不应被修改的
- 解决方案:通过抽象技术,根据新的需求在原有类上进行扩展和派生。
4.3 Liskov替换原则(LSP)
4.4 接口隔离原则(ISP)
- 不能强迫客户端依赖于它们不需要的接口:只提供必需的接口
- 胖接口可分解为多个小的接口,不同的接口向不同的客户端提供服务,客户端只访问自己所需要的端口
4.5 依赖转置原则(DIP)
- 抽象的模块不应依赖于具体的模块
- 具体应依赖于抽象
- 本质是类于类交互时应该通过接口,而不是直接调用实体类
- client端不应该出现底层类