5.1可维护性的度量与构造原则

1 软件维护和演化
什么是软件维护?
▪ 软件维护在软件工程中是指在软件产品交付后对其进行修改,以纠正错误、提高性能或其他属性。
维护是软件生命周期中最困难的方面,需要高超的诊断,测试和文档记录技能。
需要测试修改是否正确,检查回归错误,新增测试用例;通常情况下需要维护的代码没有足够的文档和测试用例,工程师必须从源代码中推断出错误。

软件维护包括四个类型:
▪ 纠正性维护,占比25%——交付后对软件产品进行的反应性修改,以纠正发现的问题
▪ 适应性维护,占比21%——交付后对软件产品进行的修改,以保持软件产品在变化或变化的环境中可用
▪ 完善性维护,占比50%——软件产品交付后的改进,以提高性能或可维护性
▪ 预防性维护。占比4%——软件产品交付后的修改,以在软件产品成为有效故障之前检测并纠正其潜在故障

▪ 软件演化是软件维护中的一个术语,指的是软件最初开发,然后由于各种原因而不断更新的过程。软件的大部分成本来自于维护阶段。

雷曼的软件进化八定律
▪ 反馈系统
▪ 持续变化
▪ 持续增长
▪ 质量下降
▪ 越来越复杂
▪ 自我调节
-保持组织稳定性
-保持熟悉度

软件设计者和开发者也要考虑软件潜在的变更/扩展使得设计的灵活性和扩展性得到综合考虑,即可维护性,可扩展性和灵活性。

2 可维护性指标
▪ 可维护性-“软件系统或组件可以被修改以纠正故障、提高性能或其他属性,或适应变化的环境的容易程度”。
▪ 可扩展性-软件设计/实现考虑到未来的增长,并被视为扩展系统的能力和实现扩展所需的努力程度的系统度量。
▪ 灵活性—软件能够根据用户需求、外部技术和社会环境等轻松地进行更改的能力。
▪ 适应性-一个交互系统(适应性系统)的能力,它可以根据获得的有关其用户及其环境的信息,使其行为适应单个用户。
▪ 可管理性-软件系统如何有效且容易地被监控和维护,以保持系统的运行、安全和平稳运行。
▪ 可支持性-基于资源(包括质量文档、诊断信息以及知识渊博和可用的技术人员),软件在部署后如何有效地保持运行。

一些常用的可维护性度量
▪ **循环复杂度-**度量代码的结构复杂度。
–它是通过计算程序流中不同代码路径的数量创建的。具有复杂控制流的程序需要更多的测试来实现良好的代码覆盖率,并且不易维护。
–CC=E-N+2,CC=P+1,CC=区域数
代码行数-表示代码中的行数。
–非常高的数目可能表示某个类型或方法试图做太多的工作,应将其拆分。
–它还可能表示类型或方法可能很难维护。

▪ 可维护性索引(MI)
-计算0到100之间的索引值,该值表示维护代码的相对容易程度。
▪ 高值意味着更好的可维护性。它的计算基础是:
–Halstead Volume(HV)
–圈复杂度(CC)
–每个模块的平均代码行数(LOC)
–每个模块的注释行百分比(COM)。
在这里插入图片描述
继承层次度-指示扩展到类层次结构的根的类定义数。层次结构越深,就越难理解在何处定义或/和重新定义特定的方法和字段。
类耦合度-通过参数、局部变量、返回类型、方法调用、泛型或模板实例化、基类、接口实现、在外部类型上定义的字段和属性修饰来测量与唯一类的耦合。
–良好的软件设计要求类型和方法具有高内聚性和低耦合性。
–高耦合表示设计很难重用和维护,因为它与其他类型有许多相互依赖关系。
单元测试覆盖率-指示自动单元测试覆盖代码库的哪个部分。

3 模块化设计和模块化原则
▪ 模块化编程是一种设计技术,它强调将程序的功能分成独立的、可互换的模块,这样每个模块都包含只执行所需功能的一个方面所需的一切。
▪ 在结构化编程和面向对象编程中,将整个程序的代码分解为多个片段
▪ 目标是将系统划分为模块,并在各个组件之间分配职责,即:模块内部的高内聚性;模块之间的松耦合;
▪ 模块化降低了程序员的总复杂度,假设:–函数被分配给模块,这些模块将相似的函数分组在一起(关注点分离)分离关注注注模块之间有小的、简单的、定义良好的接口(信息隐藏)信息隐
▪ 内聚和耦合原则可能是评估设计可维护性的最重要的设计原则。

(1) 评价模块性的五个标准
可分解性:将大问题分解为可以单独解决的小问题,类似于分治算法,这是为了最低限度的减少模块之间的依赖关系
可组合性:使得小模块能搭建除新的系统,最大限度的重用已有的模块
可理解性:人能理解模块的含义
连续性:微小的变化不会影响整体
保护性:异常发生后影响到的模块尽量少。

(2)模块化设计的五个原则
直接映射:模块的结构与现实世界中问题领域的结构保持一致
影响:–连续性 –更容易评估和限制变更的影响
–可分解性 模块化设计的可分解是软件可分解的基础
少接口
▪ 每个模块应尽可能少地与其他模块通信
–影响:连续性、保护性、可理解性和可组合性
单个接口中的内容尽可能少: 如果两个模块通信,它们应该交换尽可能少的信息
影响:可持续性,保护性
显式接口:两个模块相互交流时必须显而易见
影响:分解性、可组合性、连续性、可理解性
信息隐藏:可能发生变化的设计应该隐藏
影响:连续性

(3) 耦合与内聚

▪ 耦合是模块之间依赖性的度量。如果一个模块中的更改可能需要另一个模块中的更改,则两个模块之间存在依赖关系。
▪ 模块之间的耦合程度由以下因素决定:
–模块之间的接口数量(数量)和每个接口的复杂性(由通信类型决定)(质量)

▪ 内聚性是衡量模块的功能或职责之间关联程度的一个指标。
▪ 如果模块的所有元素都朝着同一个目标工作,则该模块具有很高的内聚性。

一个好的设计应该是高内聚低耦合的

4面向对象设计原则 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 接口聚合原则

(1) (SRP)单一责任原则
一个类应该只负责一件事情,否则就拆分它。
如果负责了太多事情就会有很多导致它变化的原因,会难以维护,占用过多资源。

反例:在这里插入图片描述
(2) (OCP)(面向变化的)开放/封闭原则
类应该对扩展开放,对修改关闭
对扩展开放是为了满足的需求,对修改关闭是为了防止内部的修改导致不稳定和错误

做到这一点关键的方法就是抽象:
“软件实体(类、模块、函数等)应该为扩展而打开,但为修改而关闭”,即,使用继承和组合/委派更改类的行为

在这里插入图片描述
(3) (LSP)Liskov替换原则
子类型必须能在任何场合下代替其父类型

(4 (ISP)接口隔离原则
不要强制类实现他们不需要实现的方法
做到这一点需要避免"胖接口",胖接口臃肿且效率低下,应当分解为多个小接口。
在这里插入图片描述

(5)(DIP)依赖倒置原则
高层模块不应该依赖于低层 模块,二者都应该依赖于抽象。
抽象不应该依赖于实现细节,实现细节应该依赖于抽象
在这里插入图片描述
委托的时候需要通过接口建立联系,而不是具体子类。

总结一下,就是需要让单一的类的责任保持单一,接口保持稳定,减少耦合,提高内聚,隔离变化,减少修改

5 面向对象设计原则:GRASP
为类和对象指派职责的原则,简称GRASP,是一种基于将责任分配给类的模式的设计方法

责任:
Object的责任:与其义务有关
需要了解其private数据,与其相关的对象,该Object可以派生的内容
需要做其负责的的事情,并协调相关对象
在这里插入图片描述
责任是通过以下方法实现的:makePayment意味着Sale对象有责任创建一个Payment对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值