目录

10 系统运维

10-1 软件维护的基本概念-1

第1关:软件维护的定义

第2关:软件维护的特点

第3关:软件维护的过程

第4关:软件的可维护性

10-2 软件维护的基本概念-2

第1关:维护的文档

第2关:可维护性复审

第3关:预防性维护

第4关:软件再工程范型

10-3 一起动手维护代码

第1关:快速排序的改正性维护

第2关:数列计算的预防性维护

第3关:P大邮箱的完善性维护


10 系统运维

10-1 软件维护的基本概念-1

第1关:软件维护的定义

任务描述
本关任务:
通过了解并学习软件维护(Software maintenance)的定义以及相关内容,通过一个案例完成右边选择题。

案例:https://github.com/aosabook/500lines 是一个github上26k star的开源项目,是《500 Lines or Less》一书的源码实现,该书是由18个项目组成的Python学习手册。
假设我们现在要fork其中的爬虫项目(https://github.com/aosabook/500lines/tree/master/crawler )
进行部署和修改。

相关知识
软件维护

定义:在软件已经交付使用之后,为了改正错误或满足新的需要而修改软件的过程。

软件维护是软件生命周期的最后一个阶段。

它的任务是:维护软件的正常运行,不断改进软件的性能和质量,为软件的进一步推广应用和更新替换做积极工作。   

软件维护所需的工作量非常大,一般说来,大型软件的维护成本高达开发总成本的四倍左右。目前,软件开发组织把60%以上的工作量用于维护自己的软件上。

软件维护的原因
通常要求进行软件维护的原因有三种:

改正在特定使用条件下暴露出来的一些潜在程序错误或设计缺陷;
因在软件使用过程中数据环境发生变化(如所要处理的数据发生变化)或处理环境发生变化(如硬件或软件操作系统等发生变化),需要修改软件,以适应这种变化;
用户和数据处理人员在使用时常提出改进现有功能、增加新功能、以及改善总体性能的要求,为满足这些要求,需要修改软件。
四类软件维护的活动
改正性维护:交付给用户使用的软件,即使通过严格的测试,仍可能有一些潜在的错误在用户使用的过程中发现和修改。诊断和改正错误的过程称为改正性维护。
适应性维护:随着计算机的飞速发展,新的硬件系统和外部设备时常更新和升级,一些数据库环境、数据输入/输出方式、数据存储介质等也可能发生变换。为了使软件适应这些环境变化而修改软件的过程叫做适应性维护。
完善性维护:在软件投入使用过程中,用户可能还会有新的功能和性能要求,可能会提出增加新功能、修改现有功能等要求。为了满足这类要求而进行的维护称为完善性维护。
预防性维护:为了改进软件未来的可维护性或可靠性,或者为了给未来的改进奠定更好的基础而进行的修改,称为预防性维护。这种维护活动在实践中比较少见。

第2关:软件维护的特点

任务描述
本关任务:通过了解并学习软件维护的特点以及相关内容,完成右边选择题。

相关知识
软件维护的3个特点

结构化维护与非结构化维护的差别巨大
非结构化维护:软件配置的唯一成分是代码,维护从评价程序代码开始,对软件结构、数据结构、系统接口、设计约束等常产生误解,不能进行回归测试,维护代价大。
结构化维护:有完整的软件配置,维护从评价设计文档开始,确定软件结构、性能和接口特点,先修改设计,接着修改代码,再进行回归测试。
软件维护的代价高昂
软件维护的代价表现为有形代价和无形代价。

有形代价:

有形代价指软件维护的费用开支;
70年代,用于软件维护的费用只占软件总预算的30%~40%,80年代上升到60%左右,90年代许多软件项目的维护经费预算达到了80%。
无形代价:

当一些看起来合理的要求不能及时满足时,会引起用户的不满;
改动软件可能会引入新的错误,使软件质量下降;
把许多软件工程师调去从事维护工作,势必影响开发工作。
维护的问题很多
理解别人写的程序通常非常困难,而且困难程度随着软件配置成分的减少而迅速增加。如果仅有程序代码没有说明文档,则会出现严重的问题;
需要维护的软件往往没有合格的文档,或者文档资料显著不足。认识到软件必须有文档仅仅是第一步,容易理解的并且和程序代码完全一致的文档才真正有价值;
当要求对软件进行维护时,不能指望由开发人员给我们仔细说明软件。由于维护阶段持续的时间很长,因此,当需要解释软件时,往往原来写程序的人已经不在附近了;
绝大多数软件在设计时 没有考虑将来的修改 。除非使用强调模块独立原理的设计方法学,否则修改软件既困难又容易发生差错;
软件维护不是一项吸引人的工作 。形成这种观念很大程度上是因为维护工作经常遭受挫折。
软件维护的工作量模型
软件维护所花费的工作量,一部分用于生产性活动,如分析、评价、修改设计、编写程序等;另一部分用于非生产性活动,如理解代码的含义、解释数据结构和接口特点等。 
Belady和Lehman提出了一种维护工作量模型

 其中:
    M:用于维护工作的总工作量;
    P:生产性工作量;
    K:经验常数;
    c:因缺乏好的设计和文档而导致软件复杂性的度量;
    d:维护人员对软件熟悉程度的度量。 

上述模型指出:如果使用了不好的软件开发方法,原来参加开发的人员或小组不能参加维护,则工作量和成本将按指数级增加。

软件维护的典型问题
如果维护时只有程序代码而没有注释说明,维护起来就相当困难;
由于软件维护阶段时间长,软件开发人员经常流动,所以在维护时,不可能所有的维护工作都依靠原来的开发人员。这会使得维护工作量增加;
软件没有足够的文档资料,或者程序修改后与文档资料不一致;
绝大多数软件在设计时没有考虑将来的修改,所以建议采用功能独立的模块化设计原则,增加软件的可维护性;
软件维护被许多人视为一种毫无吸引力的工作,因为维护工作常常受到挫折。

C,D,A,D

第3关:软件维护的过程

任务描述
本关任务:通过了解并学习软件维护的过程以及相关内容,完成右边选择题。

相关知识
软件维护过程

维护过程本质上是修改和压缩了的软件定义和开发过程,而且事实上远在提出一项维护要求之前,与软件维护有关的工作已经开始了。

首先必须建立一个维护组织。
随后必须确定报告和评价的过程,而且必须为每个维护要求规定一个标准化的事件序列。
此外,还应该建立一个适用于维护活动的记录保管过程,并且规定复审标准 。
维护组织
一般软件维护的组织如下图所示:

维护报告
根据软件问题报告(维护要求),作出的软件修改报告包含的信息主要有:

满足维护要求表中提出的要求所需要的工作量;
维护要求的性质;
这项要求的优先次序;
与修改有关的事后数据(如测试数据等)。
维护的事件流
通常维护的事件流如下图所示:

维护记录的内容
1)程序标识;                           2)源语句数;
3)机器指令数;                       4)使用的程序设计语言;
5)程序安装的日期;               6)自安装以来程序运行次数;
7)自安装以来程序失效次数   8)程序变动的层次和标识;
9)因程序变动而增加的源语句数;10)因程序变动而删除的源语句数;
11)每个改动耗费的人时数; 12)程序改动的日期;
13)软件工程师的名字;         14)维护要求表的标识;
15)维护类型;                         16)维护开始和完成的日期;
17)累计用于维护的人时数; 18)与完成的维护相联系的纯效益。

评价维护活动的7个维度
(1) 每次程序运行平均失效的次数;
(2) 用于每一类维护活动的总人时数;
(3) 平均每个程序、每种语言、每种维护类型所做的程序变动数;
(4) 维护过程中增加或删除一个源语句平均花费的人时数;
(5) 维护每种语言平均花费的人时数;
(6) 一张维护要求表的平均周转时间;
(7) 不同维护类型所占的百分比。

第4关:软件的可维护性

任务描述
本关任务:通过了解并学习软件维护的过程以及相关内容,完成右边选择题。

相关知识
决定软件可维护性的因素
软件可维护性是:维护人员理解、改正和改进软件的难易程度。
一个软件的可维护性,主要由五个因素决定:

可理解性:可理解性表现为外来读者理解软件的结构、接口、功能和内部过程的难易程度。
可测试性:在设计开发阶段应该注意尽量把软件设计成容易测试和容易诊断的,可用的测试工具和调试工具对测试和诊断非常重要。
可修改性:软件的可修改程度与软件设计阶段采用的原则和策略是直接相关的。如:模块的耦合、内聚、控制范围和作用范围、局部化程度都直接影响软件的可修改性。
可移植性:软件可移植性指的是,把程序从一种计算环境 (硬件配置和操作系统) 转移到另一种计算环境的难易程度 。把与硬件、操作系统以及其他外部设备有关的程序代码集中放到特定的程序模块中,可以把因环境变化而必须修改的程序局限在少数程序模块中,从而降低修改的难度。
可重用性:很容易修改可重用的软件构件使之再次应用在新环境中,因此,软件中使用的 可重用构件越多,适应性和完善性维护也就越容易 。
决定软件可维护性的最终因素是软件设计阶段所采用的方法,以及软件文档资料的好坏。提高软件的可维护性是软件工程的一个重要目标。 

影响维护工作量的因素
1)系统大小。系统越大,功能越复杂,理解掌握起来就越困难,需要的维护工作量越大。
2)程序设计语言。使用功能强的程序设计语言可以控制程序的规模。语言的功能越强,生成程序所需的指令数就越少;语言的功能越弱,实现同样功能所需的语句就越多,程序就越大,维护起来就越困难。
3)系统年龄。老系统比新系统需要更多的维护工作量。许多老系统在当初并未按照软件工程的要求进行开发,没有文档,或文档太少,或者在长期的维护中许多地方与程序不一致,维护起来困难较大。
4)数据库技术的应用。使用数据库工具,可有效地管理和存储用户程序中的数据,可方便地修改、扩充报表。数据库技术的使用可以减少维护工作量。
5)先进的软件开发技术。在软件开发时,如果使用能使软件结构比较稳定的分析与设计技术(如面向对象分析、设计技术),可以减少一定的工作量。
6)其它。如,应用的类型、数学模型、任务的难度、IF嵌套深度等等都会对维护工作量产生一定的影响。

逆向工程和软件再工程的区别
软件再工程(Re-engineering), 再工程不仅能从已有的程序中重新获得设计信息,而且还能使用这些信息改建或重构现有的系统。

逆向工程(Reverse Engineering),用于软件的起始建造阶段,而Re-engineering用于软件后续的修改阶段。

10-2 软件维护的基本概念-2

第1关:维护的文档

任务描述
本关任务:通过了解并学习软件维护的文档以及相关内容,通过一个案例完成右边选择题。

相关知识
文档

文档是影响软件可维护性的决定因素。 由于长期使用的大型软件系统在使用过程中必然会经受多次修改,所以文档比程序代码更重要。

软件系统的文档可以分为用户文档和系统文档两类。用户文档主要描述系统功能和使用方法,并不关心这些功能是怎样实现的;系统文档描述系统设计、实现和测试等各方面的内容。总的说来,软件文档应该满足下述要求:

必须描述如何使用这个系统,没有这种描述时即使是最简单的系统也无法使用;
必须描述怎样安装和管理这个系统;
必须描述系统需求和设计;
必须描述系统的实现和测试,以便使系统成为可维护的;
用户文档
用户文档是用户了解系统的第一步,它应该能使用户获得对系统的准确的初步印象。文档的结构方式应该使用户能够方便地根据需要阅读有关的内容。

用户文档至少应该包括下述5方面的内容 :
(1) 功能描述:说明系统能做什么;
(2) 安装文档:说明怎样安装这个系统以及怎样使系统适应特定的硬件配置;
(3) 使用手册:简要说明如何着手使用这个系统( 应该通过丰富例子说明怎样使用常用的系统功能,还应该说明用户操作错误时怎样恢复和重新启动); 
(4) 参考手册:详尽描述用户可以使用的所有系统设施以及它们的使用方法,还应该解释系统可能产生的各种出错信息的含义;
(5) 操作员指南( 如果需要有系统操作员的话) :说明操作员应该如何处理使用中出现的各种情况 。

系统文档
系统文档:从问题定义、需求说明到验收测试计划这样 一系列和系统实现有关的文档 。描述系统设计、实现和测试的文档对于理解程序和维护程序来说是极端重要的。和用户文档类似,系统文档的结构也应该能把读者从对系统概貌的了解,引导到对 系统每个方面每个特点的更形式化更具体的认识 。

第2关:可维护性复审

任务描述
本关任务:通过了解并学习软件维护的可维护性复审以及相关内容,通过一个案例完成右边选择题。

相关知识
可维护性复审

可维护性是所有软件都应该具备的基本特点,必须在开发阶段保证软件具有可维护因素。

在软件工程过程的每一个阶段都应该考虑并努力提高软件的可维护性,在每个阶段结束前的技术审查和管理复审中,应该着重对可维护性进行复审。

不同阶段的复审
需求分析阶段的复审: 应该对将来要改进的部分和可能会修改的部分加以注意并指明;应该讨论软件的可移植性问题,并且考虑可能影响软件维护的系统界面。

正式的和非正式的设计复审:应该从容易修改、模块化和功能独立的目标出发,评价软件的结构和过程;设计中应该对将来可能修改的部分预作准备。

代码复审:应该 强调编码风格和内部说明文档这两个影响可维护性的因素。

配置复审:在测试结束时进行最正式的可维护性复审。每个测试步骤都可以暗示在软件正式交付使用前,程序中可能需要做预防性维护的部分。配置复审的目的是保证软件配置的所有成分是完整的、一致的和可理解的,而且为了便于修改和管理已经编目归档了。

在完成了每项维护工作之后,都应该对软件维护本身进行仔细认真的复审。

注意:维护应该针对 整个软件配置 ,不应该只修改源程序代码。当对源程序代码的修改没有反映在设计文档或用户手册中时,就会产生严重的后果。

第3关:预防性维护

任务描述
本关任务:通过了解并学习软件维护的预防性维护(Preventive Maintenance,PM)以及相关内容,完成右边选择题。

相关知识
预防性维护的背景

几乎所有历史比较悠久的软件开发组织,都有一些十几年前开发出的 “ 老 ” 程序 。目前,某些老程序仍然在为用户服务,但是,当初开发这些程序时并没有使用软件工程方法学来指导,因此,这些程序的体系结构和数据结构都很差,文档不全甚至完全没有文档,对曾经做过的修改也没有完整的记录。怎样满足用户对上述这类老程序的维护要求呢?

为了修改这类程序以适应用户新的或变更的需求,有以下几种做法可供选择:

反复多次地做修改程序的尝试,与不可见的设计及源代码顽强战斗,以实现所要求的修改;
通过仔细分析程序尽可能多地掌握程序的内部工作细节,以便更有效地修改它;
在深入理解原有设计的基础上,用软件工程方法重新设计、重新编码和测试 那些需要变更的软件部分;
以软件工程方法学为指导,对程序 全部重新设计、重新编码和测试 ,为此可以使用CASE 工具(逆向工程和再工程工具)来帮助理解原有的设计。
预防性维护
以上第一种做法很盲目,通常人们采用后3种做法。其中第4种做法称为软件再工程,这样的维护活动就是预防性维护,而第3种做法实质上是局部的再工程。

预防性维护方法是由Miller提出来的,他把这种方法定义为:“把今天的方法学应用到昨天的系统上,以支持明天的需求。”

粗看起来,在一个正在工作的程序版本已经存在的情况下 重新开发一个大型程序,似乎是一种浪费。其实不然 ,下述事实很能说明问题:

维护一行源代码的代价可能是最初开发 该行源代码代价的14~40倍;
重新设计软件体系结构(程序及数据结构)时 使用了现代设计概念 ,它对将来的维护可能有很大的帮助;
由于现有的程序版本可作为软件原型使用,开发生产率可大大高于平均水平;
用户 具有较多使用该软件的经验,因此,能够很容易地搞清新的变更需求和变更的范围;
利用逆向工程和再工程的工具,可以使一部分工作自动化 ;
在完成预防性维护的过程中可以建立起完整的软件配置 。

第4关:软件再工程范型

任务描述
本关任务:通过了解并学习软件维护的软件再工程范型以及相关内容,完成右边选择题。

相关知识
软件再工程过程

再工程范型是一个循环模型 。这意味着作为 该范型的组成部分的每个活动
都可能被重复 ,而且对于任意一个特定的循环来说, 过程可以在完成任意
一个活动之后终止 。如下图所示:

软件再工程范型的6类活动
库存目录分析
每个软件组织都应该保存其拥有的所有应用系统的库存目录。该目录包含关于 每个应用系统的基本信息(例如,应用系统的名字,最初构建它的日期,已做过的实质性修改次数,过去18个月报告的错误,用户数量,安装它的机器数量,它的复杂程度,文档质量,整体可维护性等级,预期寿命,在未来36个月内的预期修改次数,业务重要程度等)。
下述3类程序有可能成为预防性维护的对象:

预定将使用多年的程序;
当前正在成功地使用着的程序;
在最近的将来可能要做重大修改或增强的程序。
文档重构
老程序固有的特点是缺乏文档。具体情况不同,处理这个问题的方法也不同:

为了便于今后的维护,必须更新文档,但是由于资源有限,应采用 “使用时建文档” 的方法,也就是说,不是一下子把某应用系统的文档全部都重建起来,而是只针对系统中当前正在修改的那些部分建立完整文档。随着时间流逝,将得到一组有用的和相关的文档。
如果某应用系统是完成业务工作的关键,而且必须重构全部文档,则仍然应该 设法把文档工作减少到必需的最小量 。
建立文档非常耗费时间,不可能为数百个程序都重新建立文档 如果一个程序是相对稳定的,正在走向其有用生命的终点 ,而且可能不会再经历什么变化,那么, 让它保持现状。
逆向工程
软件的逆向工程是分析程序以便在比源代码更高的抽象层次上创建出程序的某种表示的过程,也就是说,逆向工程是一个恢复设计结果 的过程,逆向工程工具从现存的程序代码中抽取有关数据、体系结构和处理过程的设计信息。

代码重构
代码重构(Code Refactoring)是最常见的再工程活动。某些老程序具有比较完整、合理的体系结构,但是,个体模块的编码方式却是难于理解、测试和维护的。在这种情况下,可以 重构可疑模块的代码。

如果重构扩展到模块边界之外并涉及软件体系结构,则重构变成了正向工程。

数据重构
定义:对数据体系结构差的程序很难进行适应性修改和增强。事实上,对许多应用系统来说,数据体系结构比源代码本身对程序的长期生存力有更大影响。

与代码重构不同,数据重构(Data Refactoring)发生在相当低的抽象层次上,它是一种全范围的再工程活动。在大多数情况下,数据重构始于逆向工程活动,分解当前使用的数据体系结构,必要时定义数据模型,标识数据对象和属性,并从软件质量的角度复审现存的数据结构。

正向工程
正向工程也称为革新或改造,这项活动不仅从现有程序中恢复设计信息,而且使用该信息去改变或重构现有系统,以提高其整体质量。

10-3 一起动手维护代码

第1关:快速排序的改正性维护

任务描述
本关任务:快速排序是一个非常经典的排序算法,能在O(nlogn)的平均时间复杂度内将一堆乱序的数排序。某天小P发现以前自己写的软件中的快速排序的函数接口,不能处理某些情况的数据。
现在我们需要维护原来的代码,修改之前的接口函数的潜在错误,这被称为改正性性维护。

输入格式
输入共两行,第一行包含整数n。
第二行包含n个整数(所有整数均在 1∼10 
9
  范围内),表示整个数列。

输出格式
输出共一行,包含n个整数,表示排好序的数列。

数据范围
1≤n≤100000

输入样例:

5
3 1 2 4 5
输出样例

1 2 3 4 5
解释:第一行输入5,表示接下来有5个数,然后输入接下来的5个数,输出排序后的结构

相关知识
为了完成本关任务,你需要掌握:1.读取数据,2.快速排序。

读取数据
在C++中,可以使用cin读取数据

第2关:数列计算的预防性维护

任务描述
本关任务:斐波那契数列数列的计算是一个经典的递归问题,但是递归计算的节点个数是O(2 
n
 ) 的级别的,存在大量重复计算。时间复杂度是O(2 
n
 ),一秒内大约能算到第三四十项。
为了改进未来的可维护性或可靠性,或为了给未来的改进奠定更好的基础而修改软件,这被称为软件的预防性维护。
请你实现更加高效的方式,输入一个整数n,求斐波那契数列的第n项。
假定从0开始,第0项为0。为了防止结果溢出int范围,结果需要对1000000007取模。
输入样例:

5
输出样例

5
解释:输入5,表示求斐波那契数列数列的第五项,0,1,1,2,3,5...第5项为5,则输出5.

编程要求
根据提示,在右侧编辑器补充代码,计算并输出结果。

第3关:P大邮箱的完善性维护

任务描述
本关任务:在P大的校园网门户中,可以设置除学号外的个性化邮箱名称。
每个邮箱地址都包含一个用户名和一个域名,用@隔开。
例如,在alice@pku.edu.cn中,alice是用户名,pku.edu.cn是域名。
某天,管理门户的老师要求对设置个性化邮箱名称时添加两条过滤规则:

用户名中的.会被忽略掉,例如alice.z@pku.edu.cn和alicez@pku.edu.cn被视为相同邮箱账号;
用户名中+及其后面的字符会被忽略掉,例如m.y+name@pku.edu.cn和my@pku.edu.cn被视为相同邮箱账号;
这两种规则可以同时被使用。
现在我们需要维护原来的代码,添加上新的功能,这被称为完善性维护。
请你维护代码,完善numUniqueEmails函数,使得给出一个邮箱地址列表emails,能返回唯一的邮箱地址数量。

样例:

相关知识
为了完成本关任务,你需要掌握:1.字符串的处理, 2.哈希表的使用方法

C++中的字符串
通过string关键字声明string对象,常用的函数有size(),substr()等
具体可以查看C++官方文档。

输出:

C++中的哈希表
通过unordered_set<string>声明一个无序集合容器,只存储不同的元素。可以在O(1)的时间复杂度内插入和删除。

示例如下:

输出:

如何求出答案
对于给出的2个新的需求,我们需要对每个字符串按照过滤规则进行处理。
对于唯一性,我们使用哈希表进行记录即可,返回哈希表的大小,即为唯一的邮箱地址。

编程要求
根据提示,在右侧编辑器补充代码,计算并输出正确答案。

Logo

一站式 AI 云服务平台

更多推荐