软件维护:修复错误、改善性能
在修复错误后需要:
- 测试所做的修改
- 回归测试
- 记录变化
怎么才能最小化错误? - 除了修复问题,修改中不能引入新的故障
最大的问题:修改后没有足够的文档记录和测试 - 操作工程师必须从源代码本身推断出避免引入回归错误所需的所有信息。
软件演化:对软件进行持续的更新
软件的大部分成本来自于维护阶段
“变化”在软件生命周期中是不可避免的!如何在最初的设计中充分考虑到未来的变化,避免因为频繁变化导致软件复杂度的增加和质量的下降?
因此
- 软件维护不仅仅是运维工程师的工作,而是从设计和开 发阶段就开始了
- 在设计与开发阶段就要考虑将来的可维护性
- 这就是“maintainability”和“extensibility”
度量可维护性
可维护性表现在以下方面
- 可维护性
- “修改软件系统或组件以纠正错误、提高性能或其他属性或适应变化的环境的容易程度”
- 可扩展性
- 软件设计/实现考虑了未来的增长,并被视为扩展系统的能力和实现扩展所需的工作水平的系统度量
- 灵活性
- 软件根据用户需求、外部技术和社会环境等进行轻松更改的能力
- 可适应性
- 系统的交互能力(自适应系统)能够根据获得的有关其用户和环境的信息,使其行为适应个别用户。
- 可管理性
- 如何有效和容易地监控和维护软件系统,以保持系统的性能、安全性和平稳运行
- 支持性
- 基于包括质量文档、诊断信息和知识渊博的可用技术人员在内的资源,在部署后保持软件运行的效率如何。
对于可维护性,有几个衡量的角度:
- Cyclomatic Complexity 圈复杂度
- 流程太复杂,意味着覆盖率测试会很麻烦,而且扩展很麻烦
- 代码行数
- 越多的代码,意味着这一段代码执行了很多操作,应该拆分
- 可维护性指数
- 继承的层次数
- 类之间的耦合度
- 好的软件设计要求类型和方法应该具有高内聚和低耦合
- 单元测试的覆盖度
Modular Design and Modularity Principles
目标是实现模块的高内聚、低耦合
五项模块化评估标准
- Decomposability (可分解性)
- Composability (可组合性)
- Understandability (可理解性)
- Continuity (可持续性) ——发生变化时受影响范围最小
- Protection (出现异常之后的保护) ——出现异常后受影响范围最小
五项模块设计原则
- Direct Mapping (直接映射)
- Few Interfaces (尽可能少的接口)
- Small Interfaces (尽可能小的接口)
- Explicit Interfaces (显式接口)
- Information Hiding (信息隐藏)
耦合和内聚
耦合是模块之间依赖关系的度量。如果一个模块的更改可能需要另一个模块的更改,则两个模块之间存在依赖关系。
内聚性是衡量模块的功能或职责之间的相关性有多强的指标。如果一个模块的所有元素都朝着同一个目标工作,那么这个模块就具有高内聚性。
OO Design Principles: SOLID
- (SRP) The Single Responsibility Principle 单一责任原则
- (OCP) The Open-Closed Principle 开放-封闭原则
- (LSP) The Liskov Substitution Principle Liskov替换原则
- (DIP) The Dependency Inversion Principle 依赖转置原则
- (ISP) The Interface Segregation Principle 接口隔离原则
Single Responsibility Principle (SRP)
“一个类改变的原因不应该超过一个”,也就是说,一个类应该专注于做一件事,而且只做一件事。
Responsibility: “a reason for change.” (责任:变化的原因)
Open/Closed Principle (OCP)
对扩展性的开放
- 模块的 行为应是可扩展的,从而该模块可表现出新的行为以满足需求的变化
对修改的封闭
- 但模块自身的代码是不应被修改的
- 扩展模块行为的一般途径是修改模块的内部实现
- 如果一个模块不能被修改,那么它通常被认为是具有固定的行为
一般可以通过继承的方式实现对扩展性的开放
Interface Segregation Principle (ISP)
不能强迫客户端依赖于它们不需要的接口:只提供必需的接口
![[Pasted image 20230520230021.png]]
对于图中的例子,前者的Server要同时满足三个客户端接口的调用,意味着这个Server对于单一的客户端,具有大量的接口冗余,属于胖接口。那么为了避免这种情况,将每一个客户端对应一个接口,然后通过接口来和Server进行交互
Dependency Inversion Principle (DIP)
抽象的模块不应依赖于具体 的模块
具体应依赖于抽象
个人理解是在委派的时候,加一层接口,原本是类A委派类B,但是现在在B上加一层接口C,改成类A委派接口C